зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1619005
- Update cubeb-coreaudio to 799518a. r=padenot
Pick commits: 799518a - Rework threading model (#55) 6e3e8e8 - Fix memory leak (#54) 996fcfa - Revise messages in test_switch_device 6fac4a6 - Isolate device tests (#53) b78e817 - Save the duplicate tests and compilings when running sanitizers (#52) Differential Revision: https://phabricator.services.mozilla.com/D67536 --HG-- rename : third_party/rust/cubeb-coreaudio/run_tests.sh => third_party/rust/cubeb-coreaudio/run_device_tests.sh extra : moz-landing-system : lando
This commit is contained in:
Родитель
773f537bd6
Коммит
4c786bae99
|
@ -75,7 +75,7 @@ rev = "5e870faf6f95d79d11efc813e56370ad124bbed5"
|
|||
[source."https://github.com/ChunMinChang/cubeb-coreaudio-rs"]
|
||||
git = "https://github.com/ChunMinChang/cubeb-coreaudio-rs"
|
||||
replace-with = "vendored-sources"
|
||||
rev = "4acd80233efa645ac79769f37b07d495c1b42070"
|
||||
rev = "799518a033a0c780cfdb82a4eaabfd9681cb7f3b"
|
||||
|
||||
[source.crates-io]
|
||||
replace-with = "vendored-sources"
|
||||
|
|
|
@ -686,7 +686,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "coreaudio-sys-utils"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/ChunMinChang/cubeb-coreaudio-rs?rev=4acd80233efa645ac79769f37b07d495c1b42070#4acd80233efa645ac79769f37b07d495c1b42070"
|
||||
source = "git+https://github.com/ChunMinChang/cubeb-coreaudio-rs?rev=799518a033a0c780cfdb82a4eaabfd9681cb7f3b#799518a033a0c780cfdb82a4eaabfd9681cb7f3b"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"coreaudio-sys",
|
||||
|
@ -923,7 +923,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "cubeb-coreaudio"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/ChunMinChang/cubeb-coreaudio-rs?rev=4acd80233efa645ac79769f37b07d495c1b42070#4acd80233efa645ac79769f37b07d495c1b42070"
|
||||
source = "git+https://github.com/ChunMinChang/cubeb-coreaudio-rs?rev=799518a033a0c780cfdb82a4eaabfd9681cb7f3b#799518a033a0c780cfdb82a4eaabfd9681cb7f3b"
|
||||
dependencies = [
|
||||
"atomic",
|
||||
"audio-mixer",
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"35acbd2f8633a6109f3d3e554bef8d847c049ce6ef7a5f570468819e41344d7f","src/aggregate_device.rs":"7d2bd5f5fd7f3d008ebb69ad81f522ca0cb73db6d7b3e50ed1a63ea26ff721f4","src/audio_object.rs":"df10160d9fd83a2c23a49e69b78d39db3a9d6389607df6acfc05821293b6af5f","src/audio_unit.rs":"d783878930df4923b57ad230138c0f3fd6b0b9bb80a39725092ff4c6615162d8","src/cf_mutable_dict.rs":"fc42edd270c6dfb02f123214d2d8e487bbd62b5bd923b71eec13190fd0104d2a","src/dispatch.rs":"195ca94cbc61948637bfdcbe22070a1e6d41e97cec22301df4e45dcef7b1c208","src/lib.rs":"bcc559d69ef6ed0cbea5b2a36fec89d8c011eb9da70e2f26c00f881ad97a2546","src/string.rs":"28f88b816c768bcfcc674a60d962b93f1c94e5e0f4cc8ed2a1301138b91039e7"},"package":null}
|
||||
{"files":{"Cargo.toml":"35acbd2f8633a6109f3d3e554bef8d847c049ce6ef7a5f570468819e41344d7f","src/aggregate_device.rs":"7d2bd5f5fd7f3d008ebb69ad81f522ca0cb73db6d7b3e50ed1a63ea26ff721f4","src/audio_object.rs":"df10160d9fd83a2c23a49e69b78d39db3a9d6389607df6acfc05821293b6af5f","src/audio_unit.rs":"d783878930df4923b57ad230138c0f3fd6b0b9bb80a39725092ff4c6615162d8","src/cf_mutable_dict.rs":"fc42edd270c6dfb02f123214d2d8e487bbd62b5bd923b71eec13190fd0104d2a","src/dispatch.rs":"0f4b05076bf4ce8e9ce2a98c65149fcdd716b772a7ab111f37f9d12678552e1e","src/lib.rs":"bcc559d69ef6ed0cbea5b2a36fec89d8c011eb9da70e2f26c00f881ad97a2546","src/string.rs":"28f88b816c768bcfcc674a60d962b93f1c94e5e0f4cc8ed2a1301138b91039e7"},"package":null}
|
|
@ -4,148 +4,203 @@ use std::ffi::CString;
|
|||
use std::mem;
|
||||
use std::os::raw::c_void;
|
||||
use std::ptr;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
pub const DISPATCH_QUEUE_SERIAL: dispatch_queue_attr_t = ptr::null_mut::<dispatch_queue_attr_s>();
|
||||
// Queue: A wrapper around `dispatch_queue_t`.
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
#[derive(Debug)]
|
||||
pub struct Queue(dispatch_queue_t);
|
||||
|
||||
pub fn create_dispatch_queue(
|
||||
label: &'static str,
|
||||
queue_attr: dispatch_queue_attr_t,
|
||||
) -> dispatch_queue_t {
|
||||
let label = CString::new(label).unwrap();
|
||||
let c_string = label.as_ptr();
|
||||
unsafe { dispatch_queue_create(c_string, queue_attr) }
|
||||
}
|
||||
|
||||
pub fn release_dispatch_queue(queue: dispatch_queue_t) {
|
||||
// TODO: This is incredibly unsafe. Find another way to release the queue.
|
||||
unsafe {
|
||||
dispatch_release(mem::transmute::<dispatch_queue_t, dispatch_object_t>(queue));
|
||||
impl Queue {
|
||||
pub fn new(label: &str) -> Self {
|
||||
const DISPATCH_QUEUE_SERIAL: dispatch_queue_attr_t =
|
||||
ptr::null_mut::<dispatch_queue_attr_s>();
|
||||
let label = CString::new(label).unwrap();
|
||||
let c_string = label.as_ptr();
|
||||
let queue = Self(unsafe { dispatch_queue_create(c_string, DISPATCH_QUEUE_SERIAL) });
|
||||
queue.set_should_cancel(Box::new(AtomicBool::new(false)));
|
||||
queue
|
||||
}
|
||||
}
|
||||
|
||||
pub fn async_dispatch<F>(queue: dispatch_queue_t, work: F)
|
||||
where
|
||||
F: Send + FnOnce(),
|
||||
{
|
||||
let (closure, executor) = create_closure_and_executor(work);
|
||||
unsafe {
|
||||
dispatch_async_f(queue, closure, executor);
|
||||
pub fn run_async<F>(&self, work: F)
|
||||
where
|
||||
F: Send + FnOnce(),
|
||||
{
|
||||
let should_cancel = self.get_should_cancel();
|
||||
let (closure, executor) = Self::create_closure_and_executor(|| {
|
||||
if should_cancel.map_or(false, |v| v.load(Ordering::SeqCst)) {
|
||||
return;
|
||||
}
|
||||
work();
|
||||
});
|
||||
unsafe {
|
||||
dispatch_async_f(self.0, closure, executor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sync_dispatch<F>(queue: dispatch_queue_t, work: F)
|
||||
where
|
||||
F: Send + FnOnce(),
|
||||
{
|
||||
let (closure, executor) = create_closure_and_executor(work);
|
||||
unsafe {
|
||||
dispatch_sync_f(queue, closure, executor);
|
||||
pub fn run_sync<F>(&self, work: F)
|
||||
where
|
||||
F: Send + FnOnce(),
|
||||
{
|
||||
let should_cancel = self.get_should_cancel();
|
||||
let (closure, executor) = Self::create_closure_and_executor(|| {
|
||||
if should_cancel.map_or(false, |v| v.load(Ordering::SeqCst)) {
|
||||
return;
|
||||
}
|
||||
work();
|
||||
});
|
||||
unsafe {
|
||||
dispatch_sync_f(self.0, closure, executor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return an raw pointer to a (unboxed) closure and an executor that
|
||||
// will run the closure (after re-boxing the closure) when it's called.
|
||||
fn create_closure_and_executor<F>(closure: F) -> (*mut c_void, dispatch_function_t)
|
||||
where
|
||||
F: FnOnce(),
|
||||
{
|
||||
extern "C" fn closure_executer<F>(unboxed_closure: *mut c_void)
|
||||
pub fn run_final<F>(&self, work: F)
|
||||
where
|
||||
F: Send + FnOnce(),
|
||||
{
|
||||
let should_cancel = self.get_should_cancel();
|
||||
let (closure, executor) = Self::create_closure_and_executor(|| {
|
||||
work();
|
||||
should_cancel
|
||||
.expect("dispatch context should be allocated!")
|
||||
.store(true, Ordering::SeqCst);
|
||||
});
|
||||
unsafe {
|
||||
dispatch_sync_f(self.0, closure, executor);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_should_cancel(&self) -> Option<&mut AtomicBool> {
|
||||
unsafe {
|
||||
let context = dispatch_get_context(
|
||||
mem::transmute::<dispatch_queue_t, dispatch_object_t>(self.0),
|
||||
) as *mut AtomicBool;
|
||||
context.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
fn set_should_cancel(&self, context: Box<AtomicBool>) {
|
||||
unsafe {
|
||||
let queue = mem::transmute::<dispatch_queue_t, dispatch_object_t>(self.0);
|
||||
// Leak the context from Box.
|
||||
dispatch_set_context(queue, Box::into_raw(context) as *mut c_void);
|
||||
|
||||
extern "C" fn finalizer(context: *mut c_void) {
|
||||
// Retake the leaked context into box and then drop it.
|
||||
let _ = unsafe { Box::from_raw(context as *mut AtomicBool) };
|
||||
}
|
||||
|
||||
// The `finalizer` is only run if the `context` in `queue` is set by `dispatch_set_context`.
|
||||
dispatch_set_finalizer_f(queue, Some(finalizer));
|
||||
}
|
||||
}
|
||||
|
||||
fn release(&self) {
|
||||
unsafe {
|
||||
// This will release the inner `dispatch_queue_t` asynchronously.
|
||||
// TODO: It's incredibly unsafe to call `transmute` directly.
|
||||
// Find another way to release the queue.
|
||||
dispatch_release(mem::transmute::<dispatch_queue_t, dispatch_object_t>(
|
||||
self.0,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
fn create_closure_and_executor<F>(closure: F) -> (*mut c_void, dispatch_function_t)
|
||||
where
|
||||
F: FnOnce(),
|
||||
{
|
||||
// Retake the leaked closure.
|
||||
let closure = unsafe { Box::from_raw(unboxed_closure as *mut F) };
|
||||
// Execute the closure.
|
||||
(*closure)();
|
||||
// closure is released after finishing this function call.
|
||||
extern "C" fn closure_executer<F>(unboxed_closure: *mut c_void)
|
||||
where
|
||||
F: FnOnce(),
|
||||
{
|
||||
// Retake the leaked closure.
|
||||
let closure = unsafe { Box::from_raw(unboxed_closure as *mut F) };
|
||||
// Execute the closure.
|
||||
(*closure)();
|
||||
// closure is released after finishing this function call.
|
||||
}
|
||||
|
||||
let closure = Box::new(closure); // Allocate closure on heap.
|
||||
let executor: dispatch_function_t = Some(closure_executer::<F>);
|
||||
|
||||
(
|
||||
Box::into_raw(closure) as *mut c_void, // Leak the closure.
|
||||
executor,
|
||||
)
|
||||
}
|
||||
|
||||
let closure = Box::new(closure); // Allocate closure on heap.
|
||||
let executor: dispatch_function_t = Some(closure_executer::<F>);
|
||||
|
||||
(
|
||||
Box::into_raw(closure) as *mut c_void, // Leak the closure.
|
||||
executor,
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use std::sync::{Arc, Mutex};
|
||||
const COUNT: u32 = 10;
|
||||
|
||||
#[test]
|
||||
fn test_async_dispatch() {
|
||||
use std::sync::mpsc::channel;
|
||||
|
||||
get_queue_and_resource("Run with async dispatch api wrappers", |queue, resource| {
|
||||
let (tx, rx) = channel();
|
||||
for i in 0..COUNT {
|
||||
let (res, tx) = (Arc::clone(&resource), tx.clone());
|
||||
async_dispatch(queue, move || {
|
||||
let mut res = res.lock().unwrap();
|
||||
assert_eq!(res.last_touched, if i == 0 { None } else { Some(i - 1) });
|
||||
assert_eq!(res.touched_count, i);
|
||||
res.touch(i);
|
||||
if i == COUNT - 1 {
|
||||
tx.send(()).unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
rx.recv().unwrap(); // Wait until it's touched COUNT times.
|
||||
let resource = resource.lock().unwrap();
|
||||
assert_eq!(resource.touched_count, COUNT);
|
||||
assert_eq!(resource.last_touched.unwrap(), COUNT - 1);
|
||||
});
|
||||
impl Drop for Queue {
|
||||
fn drop(&mut self) {
|
||||
self.release();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sync_dispatch() {
|
||||
get_queue_and_resource("Run with sync dispatch api wrappers", |queue, resource| {
|
||||
for i in 0..COUNT {
|
||||
let res = Arc::clone(&resource);
|
||||
sync_dispatch(queue, move || {
|
||||
let mut res = res.lock().unwrap();
|
||||
assert_eq!(res.last_touched, if i == 0 { None } else { Some(i - 1) });
|
||||
assert_eq!(res.touched_count, i);
|
||||
res.touch(i);
|
||||
});
|
||||
}
|
||||
let resource = resource.lock().unwrap();
|
||||
assert_eq!(resource.touched_count, COUNT);
|
||||
assert_eq!(resource.last_touched.unwrap(), COUNT - 1);
|
||||
});
|
||||
}
|
||||
|
||||
struct Resource {
|
||||
last_touched: Option<u32>,
|
||||
touched_count: u32,
|
||||
}
|
||||
|
||||
impl Resource {
|
||||
fn new() -> Self {
|
||||
Resource {
|
||||
last_touched: None,
|
||||
touched_count: 0,
|
||||
}
|
||||
}
|
||||
fn touch(&mut self, who: u32) {
|
||||
self.last_touched = Some(who);
|
||||
self.touched_count += 1;
|
||||
impl Clone for Queue {
|
||||
fn clone(&self) -> Self {
|
||||
// TODO: It's incredibly unsafe to call `transmute` directly.
|
||||
// Find another way to release the queue.
|
||||
unsafe {
|
||||
dispatch_retain(mem::transmute::<dispatch_queue_t, dispatch_object_t>(
|
||||
self.0,
|
||||
));
|
||||
}
|
||||
Self(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_tasks_in_order() {
|
||||
let mut visited = Vec::<u32>::new();
|
||||
|
||||
// Rust compilter doesn't allow a pointer to be passed across threads.
|
||||
// A hacky way to do that is to cast the pointer into a value, then
|
||||
// the value, which is actually an address, can be copied into threads.
|
||||
let ptr = &mut visited as *mut Vec<u32> as usize;
|
||||
|
||||
fn visit(v: u32, visited_ptr: usize) {
|
||||
let visited = unsafe { &mut *(visited_ptr as *mut Vec<u32>) };
|
||||
visited.push(v);
|
||||
};
|
||||
|
||||
let queue = Queue::new("Run tasks in order");
|
||||
|
||||
queue.run_sync(move || visit(1, ptr));
|
||||
queue.run_sync(move || visit(2, ptr));
|
||||
queue.run_async(move || visit(3, ptr));
|
||||
queue.run_async(move || visit(4, ptr));
|
||||
// Call sync here to block the current thread and make sure all the tasks are done.
|
||||
queue.run_sync(move || visit(5, ptr));
|
||||
|
||||
assert_eq!(visited, vec![1, 2, 3, 4, 5]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_final_task() {
|
||||
let mut visited = Vec::<u32>::new();
|
||||
|
||||
fn get_queue_and_resource<F>(label: &'static str, callback: F)
|
||||
where
|
||||
F: FnOnce(dispatch_queue_t, Arc<Mutex<Resource>>),
|
||||
{
|
||||
let queue = create_dispatch_queue(label, DISPATCH_QUEUE_SERIAL);
|
||||
let resource = Arc::new(Mutex::new(Resource::new()));
|
||||
// Rust compilter doesn't allow a pointer to be passed across threads.
|
||||
// A hacky way to do that is to cast the pointer into a value, then
|
||||
// the value, which is actually an address, can be copied into threads.
|
||||
let ptr = &mut visited as *mut Vec<u32> as usize;
|
||||
|
||||
callback(queue, resource);
|
||||
fn visit(v: u32, visited_ptr: usize) {
|
||||
let visited = unsafe { &mut *(visited_ptr as *mut Vec<u32>) };
|
||||
visited.push(v);
|
||||
};
|
||||
|
||||
// Release the queue.
|
||||
release_dispatch_queue(queue);
|
||||
let queue = Queue::new("Task after run_final will be cancelled");
|
||||
|
||||
queue.run_sync(move || visit(1, ptr));
|
||||
queue.run_async(move || visit(2, ptr));
|
||||
queue.run_final(move || visit(3, ptr));
|
||||
queue.run_async(move || visit(4, ptr));
|
||||
queue.run_sync(move || visit(5, ptr));
|
||||
}
|
||||
// `queue` will be dropped asynchronously and then the `finalizer` of the `queue`
|
||||
// should be fired to clean up the `context` set in the `queue`.
|
||||
|
||||
assert_eq!(visited, vec![1, 2, 3]);
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{".editorconfig":"4e53b182bcc78b83d7e1b5c03efa14d22d4955c4ed2514d1ba4e99c1eb1a50ba",".travis.yml":"878d9519da0844cd3de2999f81fce966452f0fb65150605c25a1abf3523360ab","Cargo.toml":"bcb3ec3785c3cbe799bb41c07176afdd0028328be79932a4e44bc97a364e8c69","LICENSE":"6e6f56aff5bbf3cbc60747e152fb1a719bd0716aaf6d711c554f57d92e96297c","README.md":"fa323b7386a8a0c75478b77a30a3c5d33f1df23d9775b97570fa0501ef784e95","install_rustfmt_clippy.sh":"4ae90d8dcb9757cb3ae4ae142ef80e5377c0dde61c63f4a3c32418646e80ca7b","run_sanitizers.sh":"913efec447b4c37751af93eef4a974f293426c20c720d37c5bbe69e955d105eb","run_tests.sh":"fcdae57b81426c5dbfc8873a9ff1ac07924ed0717f822b50a67934cac2e9d4f1","src/backend/aggregate_device.rs":"ae21129aa6b3c7bd3376751b6a94d1ebe6c9f7afcd1db3107fb4d703d04da6b3","src/backend/auto_array.rs":"5f35545baba2b005e13a2225bd1cbdd94ffc2097554d61479929bfc5442a6dd6","src/backend/auto_release.rs":"050fdcee74cf46b9a8a85a877e166d72a853d33220f59cf734cbb6ea09daa441","src/backend/device_property.rs":"324a7c9672daa49ee2221a56d140e1c8298719dab66f204b19d10f3632f96602","src/backend/mixer.rs":"0c237bd5ca63b028c4b22ddc5bc026d7e21c0fa9b4e337f00b6131ed0a0806a5","src/backend/mod.rs":"250cf24aabe1f1e9e8eafd375a538554523accaa6b514588b157d0c413d39b86","src/backend/resampler.rs":"fd1281d28a4db1659d2f75e43b8457651745e1b6eb5a53a77f04d752135f6dc7","src/backend/tests/aggregate_device.rs":"107f5c637844cd5ae43d2b42cec4ef3369bb702751586078c0a9d50f039161cd","src/backend/tests/api.rs":"d81128f0982ecc816666e91f57e722cc21ba9cc09a2a36103acc7c981f57b36d","src/backend/tests/backlog.rs":"3b189a7e036543c467cc242af0ed3332721179ee2b1c8847a6db563546f1ac52","src/backend/tests/device_change.rs":"2138e7ed4721872ce3a41f3652bfa4e7eca97fd136473af6472313c61ff24ed3","src/backend/tests/device_property.rs":"b1a9ae79aa5b9a3f180040d0ef0954b134680d586882d2062c5e017b555bff57","src/backend/tests/interfaces.rs":"14943e84a79976a7ef52882edeb9330350705d5190db6647f98b4ffa851a8396","src/backend/tests/manual.rs":"dc707836dab31f83d4b325afbc4dc4c8104ac8036e87f59ade3309ee83fe2d3f","src/backend/tests/mod.rs":"8dba770023d7f9c4228f0e11915347f0e07da5fd818e3ee4478c4b197af9aa2a","src/backend/tests/parallel.rs":"f9e1883660d6146b6e5075806561f5f689810e25c5e7764dfd28c9b939821a49","src/backend/tests/tone.rs":"16150438317ce501986734167b5fb97bfec567228acbcd8f3b4c4484c22f29e0","src/backend/tests/utils.rs":"1bb99ef71d3c18545bca49767e7b6bfffbe11896246994f41be7ed372772fd48","src/backend/utils.rs":"5ce1b753af0ffb654b6b2038d649aea88eebd27390a607a6d5988a9e40a4a717","src/capi.rs":"21b66b70545bf04ec719928004d1d9adb45b24ced51288f5b2993d79aaf78f5f","src/lib.rs":"5e586d45cd6b3722f0a6736d9252593299269817a153eef1930a5fb9bfbb56f5","todo.md":"29545b4d9c516396f82bd392797e2713d4602036eaba0f151b384af764f8515f"},"package":null}
|
||||
{"files":{".editorconfig":"4e53b182bcc78b83d7e1b5c03efa14d22d4955c4ed2514d1ba4e99c1eb1a50ba",".travis.yml":"878d9519da0844cd3de2999f81fce966452f0fb65150605c25a1abf3523360ab","Cargo.toml":"bcb3ec3785c3cbe799bb41c07176afdd0028328be79932a4e44bc97a364e8c69","LICENSE":"6e6f56aff5bbf3cbc60747e152fb1a719bd0716aaf6d711c554f57d92e96297c","README.md":"0d5a4c39e737aeeccfd5a54914e8927631ad86e3da8c24f62594f2f89a3302bf","install_rustfmt_clippy.sh":"4ae90d8dcb9757cb3ae4ae142ef80e5377c0dde61c63f4a3c32418646e80ca7b","run_device_tests.sh":"e66d32da5439818eee30fc5e7ed9ab9990723ffafe684870efe82dac11be07ef","run_sanitizers.sh":"54970203e86eed00245de3d9b53584d5d8868be983f90b27c603b310ba5554e5","run_tests.sh":"257ac9b30e0ff3775d713668a9704f15ad4ed2334aca7d8c544d079cde197e8a","src/backend/aggregate_device.rs":"ae21129aa6b3c7bd3376751b6a94d1ebe6c9f7afcd1db3107fb4d703d04da6b3","src/backend/auto_array.rs":"5f35545baba2b005e13a2225bd1cbdd94ffc2097554d61479929bfc5442a6dd6","src/backend/auto_release.rs":"050fdcee74cf46b9a8a85a877e166d72a853d33220f59cf734cbb6ea09daa441","src/backend/device_property.rs":"324a7c9672daa49ee2221a56d140e1c8298719dab66f204b19d10f3632f96602","src/backend/mixer.rs":"0c237bd5ca63b028c4b22ddc5bc026d7e21c0fa9b4e337f00b6131ed0a0806a5","src/backend/mod.rs":"e6ed4937f9ca52626a0ab6728631a88c9ae1369fd1ff61a377dfa64c3d406fb3","src/backend/resampler.rs":"fd1281d28a4db1659d2f75e43b8457651745e1b6eb5a53a77f04d752135f6dc7","src/backend/tests/aggregate_device.rs":"107f5c637844cd5ae43d2b42cec4ef3369bb702751586078c0a9d50f039161cd","src/backend/tests/api.rs":"58cbf67e3f4588f5e644ebcdc24942fcd6299c26f42a7e27fb4b6b9d8e5245f8","src/backend/tests/backlog.rs":"3b189a7e036543c467cc242af0ed3332721179ee2b1c8847a6db563546f1ac52","src/backend/tests/device_change.rs":"8261f561f69dabd374ac47d69aa484812b65070a9e9581dfb2605e8404eaad6d","src/backend/tests/device_property.rs":"b1a9ae79aa5b9a3f180040d0ef0954b134680d586882d2062c5e017b555bff57","src/backend/tests/interfaces.rs":"14943e84a79976a7ef52882edeb9330350705d5190db6647f98b4ffa851a8396","src/backend/tests/manual.rs":"dc707836dab31f83d4b325afbc4dc4c8104ac8036e87f59ade3309ee83fe2d3f","src/backend/tests/mod.rs":"8dba770023d7f9c4228f0e11915347f0e07da5fd818e3ee4478c4b197af9aa2a","src/backend/tests/parallel.rs":"f9e1883660d6146b6e5075806561f5f689810e25c5e7764dfd28c9b939821a49","src/backend/tests/tone.rs":"16150438317ce501986734167b5fb97bfec567228acbcd8f3b4c4484c22f29e0","src/backend/tests/utils.rs":"1bb99ef71d3c18545bca49767e7b6bfffbe11896246994f41be7ed372772fd48","src/backend/utils.rs":"5ce1b753af0ffb654b6b2038d649aea88eebd27390a607a6d5988a9e40a4a717","src/capi.rs":"21b66b70545bf04ec719928004d1d9adb45b24ced51288f5b2993d79aaf78f5f","src/lib.rs":"5e586d45cd6b3722f0a6736d9252593299269817a153eef1930a5fb9bfbb56f5","todo.md":"29545b4d9c516396f82bd392797e2713d4602036eaba0f151b384af764f8515f"},"package":null}
|
|
@ -43,14 +43,22 @@ by `sh run_sanitizers.sh`.
|
|||
The above command will run all the test suits in *run_tests.sh* by all the available _sanitizers_.
|
||||
However, it takes a long time for finshing the tests.
|
||||
|
||||
### Device Switching
|
||||
### Device Tests
|
||||
|
||||
Run `run_device_tests.sh`.
|
||||
|
||||
If you'd like to run all the device tests with sanitizers,
|
||||
use `RUSTFLAGS="-Z sanitizer=<SAN>" sh run_device_tests.sh`
|
||||
with valid `<SAN>` such as `address` or `thread`.
|
||||
|
||||
#### Device Switching
|
||||
|
||||
The system default device will be changed during our tests.
|
||||
All the available devices will take turns being the system default device.
|
||||
However, after finishing the tests, the default device will be set to the original one.
|
||||
The sounds in the tests should be able to continue whatever the system default device is.
|
||||
|
||||
### Device Plugging/Unplugging
|
||||
#### Device Plugging/Unplugging
|
||||
|
||||
We implement APIs simulating plugging or unplugging a device
|
||||
by adding or removing an aggregate device programmatically.
|
||||
|
@ -68,6 +76,15 @@ It's used to verify our callbacks for minitoring the system devices work.
|
|||
- Device collection change
|
||||
- `cargo test test_device_collection_change -- --ignored --nocapture`
|
||||
- Plug/Unplug devices to see events log.
|
||||
- Manual Stream Tester
|
||||
- `cargo test test_stream_tester -- --ignored --nocapture`
|
||||
- `c` to create a stream
|
||||
- `d` to destroy a stream
|
||||
- `s` to start the created stream
|
||||
- `t` to stop the created stream
|
||||
- `q` to quit the test
|
||||
- It's useful to simulate the stream bahavior to reproduce the bug we found,
|
||||
with some modified code.
|
||||
|
||||
## TODO
|
||||
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
echo "\n\nRun device-changed tests\n===================="
|
||||
|
||||
if [[ -z "${RUST_BACKTRACE}" ]]; then
|
||||
# Display backtrace for debugging
|
||||
export RUST_BACKTRACE=1
|
||||
fi
|
||||
echo "RUST_BACKTRACE is set to ${RUST_BACKTRACE}\n"
|
||||
|
||||
cargo test test_switch_device -- --ignored --nocapture
|
||||
cargo test test_plug_and_unplug_device -- --ignored --nocapture
|
||||
# cargo test test_register_device_changed_callback -- --ignored --nocapture --test-threads=1
|
||||
cargo test test_register_device_changed_callback_to_check_default_device_changed_input -- --ignored --nocapture
|
||||
cargo test test_register_device_changed_callback_to_check_default_device_changed_output -- --ignored --nocapture
|
||||
cargo test test_register_device_changed_callback_to_check_default_device_changed_duplex -- --ignored --nocapture
|
||||
cargo test test_register_device_changed_callback_to_check_input_alive_changed_input -- --ignored --nocapture
|
||||
cargo test test_register_device_changed_callback_to_check_input_alive_changed_duplex -- --ignored --nocapture
|
||||
|
||||
cargo test test_destroy_input_stream_after_unplugging_a_nondefault_input_device -- --ignored --nocapture
|
||||
cargo test test_destroy_input_stream_after_unplugging_a_default_input_device -- --ignored --nocapture
|
||||
# FIXIT: The following test will hang since we don't monitor the alive status of the output device
|
||||
# cargo test test_destroy_output_stream_after_unplugging_a_nondefault_output_device -- --ignored --nocapture
|
||||
cargo test test_destroy_output_stream_after_unplugging_a_default_output_device -- --ignored --nocapture
|
||||
cargo test test_destroy_duplex_stream_after_unplugging_a_nondefault_input_device -- --ignored --nocapture
|
||||
cargo test test_destroy_duplex_stream_after_unplugging_a_default_input_device -- --ignored --nocapture
|
||||
# FIXIT: The following test will hang since we don't monitor the alive status of the output device
|
||||
# cargo test test_destroy_duplex_stream_after_unplugging_a_nondefault_output_device -- --ignored --nocapture
|
||||
cargo test test_destroy_duplex_stream_after_unplugging_a_default_output_device -- --ignored --nocapture
|
||||
|
||||
cargo test test_reinit_input_stream_by_unplugging_a_nondefault_input_device -- --ignored --nocapture
|
||||
cargo test test_reinit_input_stream_by_unplugging_a_default_input_device -- --ignored --nocapture
|
||||
# FIXIT: The following test will hang since we don't monitor the alive status of the output device
|
||||
# cargo test test_reinit_output_stream_by_unplugging_a_nondefault_output_device -- --ignored --nocapture
|
||||
cargo test test_reinit_output_stream_by_unplugging_a_default_output_device -- --ignored --nocapture
|
||||
cargo test test_reinit_duplex_stream_by_unplugging_a_nondefault_input_device -- --ignored --nocapture
|
||||
cargo test test_reinit_duplex_stream_by_unplugging_a_default_input_device -- --ignored --nocapture
|
||||
# FIXIT: The following test will hang since we don't monitor the alive status of the output device
|
||||
# cargo test test_reinit_duplex_stream_by_unplugging_a_nondefault_output_device -- --ignored --nocapture
|
||||
cargo test test_reinit_duplex_stream_by_unplugging_a_default_output_device -- --ignored --nocapture
|
|
@ -6,42 +6,15 @@
|
|||
toolchain=$(rustup default)
|
||||
echo "\nUse Rust toolchain: $toolchain"
|
||||
|
||||
if [[ $toolchain == nightly-* ]]
|
||||
then
|
||||
echo "Run sanitizers!"
|
||||
else
|
||||
if [[ $toolchain != nightly-* ]]; then
|
||||
echo "The sanitizer is only available on Rust Nightly only. Skip."
|
||||
exit
|
||||
fi
|
||||
|
||||
# Run tests in the sub crate
|
||||
# -------------------------------------------------------------------------------------------------
|
||||
cd coreaudio-sys-utils
|
||||
|
||||
echo "\n\nRun ASan\n-----------\n"
|
||||
RUSTFLAGS="-Z sanitizer=address" cargo test
|
||||
|
||||
echo "\n\nRun LSan\n-----------\n"
|
||||
RUSTFLAGS="-Z sanitizer=leak" cargo test
|
||||
|
||||
echo "\n\nRun MSan\n-----------\n"
|
||||
RUSTFLAGS="-Z sanitizer=memory" cargo test
|
||||
|
||||
echo "\n\nRun TSan\n-----------\n"
|
||||
RUSTFLAGS="-Z sanitizer=thread" cargo test
|
||||
|
||||
cd ..
|
||||
|
||||
# Run tests in the main crate
|
||||
# -------------------------------------------------------------------------------------------------
|
||||
echo "\n\nRun ASan\n-----------\n"
|
||||
RUSTFLAGS="-Z sanitizer=address" sh run_tests.sh
|
||||
|
||||
echo "\n\nRun LSan\n-----------\n"
|
||||
RUSTFLAGS="-Z sanitizer=leak" sh run_tests.sh
|
||||
|
||||
echo "\n\nRun MSan\n-----------\n"
|
||||
RUSTFLAGS="-Z sanitizer=memory" sh run_tests.sh
|
||||
|
||||
echo "\n\nRun TSan\n-----------\n"
|
||||
RUSTFLAGS="-Z sanitizer=thread" sh run_tests.sh
|
||||
sanitizers=("address" "leak" "memory" "thread")
|
||||
for san in "${sanitizers[@]}"
|
||||
do
|
||||
San="$(tr '[:lower:]' '[:upper:]' <<< ${san:0:1})${san:1}"
|
||||
echo "\n\nRun ${San}Sanitizer\n------------------------------"
|
||||
RUSTFLAGS="-Z sanitizer=${san}" sh run_tests.sh
|
||||
done
|
||||
|
|
|
@ -1,20 +1,30 @@
|
|||
# display backtrace for debugging
|
||||
export RUST_BACKTRACE=1
|
||||
echo "\n\nTest suite for cubeb-coreaudio\n========================================"
|
||||
|
||||
if [[ -z "${RUST_BACKTRACE}" ]]; then
|
||||
# Display backtrace for debugging
|
||||
export RUST_BACKTRACE=1
|
||||
fi
|
||||
echo "RUST_BACKTRACE is set to ${RUST_BACKTRACE}\n"
|
||||
|
||||
# Run tests in the sub crate
|
||||
# Run the tests by `cargo * -p <SUB_CRATE>` if it's possible. By doing so, the duplicate compiling
|
||||
# between this crate and the <SUB_CRATE> can be saved. The compiling for <SUB_CRATE> can be reused
|
||||
# when running `cargo *` with this crate.
|
||||
# -------------------------------------------------------------------------------------------------
|
||||
cd coreaudio-sys-utils
|
||||
SUB_CRATE="coreaudio-sys-utils"
|
||||
|
||||
# Format check
|
||||
# `cargo fmt -p *` is only usable in workspaces, so a workaround is to enter to the sub crate
|
||||
# and then exit from it.
|
||||
cd $SUB_CRATE
|
||||
cargo fmt --all -- --check
|
||||
cd ..
|
||||
|
||||
# Lints check
|
||||
cargo clippy -- -D warnings
|
||||
cargo clippy -p $SUB_CRATE -- -D warnings
|
||||
|
||||
# Regular Tests
|
||||
cargo test
|
||||
|
||||
cd ..
|
||||
cargo test -p $SUB_CRATE
|
||||
|
||||
# Run tests in the main crate
|
||||
# -------------------------------------------------------------------------------------------------
|
||||
|
@ -33,36 +43,7 @@ cargo test test_aggregate -- --ignored --test-threads=1
|
|||
cargo test test_parallel -- --ignored --nocapture --test-threads=1
|
||||
|
||||
# Device-changed Tests
|
||||
cargo test test_switch_device -- --ignored --nocapture
|
||||
cargo test test_plug_and_unplug_device -- --ignored --nocapture
|
||||
# cargo test test_register_device_changed_callback -- --ignored --nocapture --test-threads=1
|
||||
cargo test test_register_device_changed_callback_to_check_default_device_changed_input -- --ignored --nocapture
|
||||
cargo test test_register_device_changed_callback_to_check_default_device_changed_output -- --ignored --nocapture
|
||||
cargo test test_register_device_changed_callback_to_check_default_device_changed_duplex -- --ignored --nocapture
|
||||
cargo test test_register_device_changed_callback_to_check_input_alive_changed_input -- --ignored --nocapture
|
||||
cargo test test_register_device_changed_callback_to_check_input_alive_changed_duplex -- --ignored --nocapture
|
||||
|
||||
cargo test test_destroy_input_stream_after_unplugging_a_nondefault_input_device -- --ignored --nocapture
|
||||
cargo test test_destroy_input_stream_after_unplugging_a_default_input_device -- --ignored --nocapture
|
||||
# FIXIT: The following test will hang since we don't monitor the alive status of the output device
|
||||
# cargo test test_destroy_output_stream_after_unplugging_a_nondefault_output_device -- --ignored --nocapture
|
||||
cargo test test_destroy_output_stream_after_unplugging_a_default_output_device -- --ignored --nocapture
|
||||
cargo test test_destroy_duplex_stream_after_unplugging_a_nondefault_input_device -- --ignored --nocapture
|
||||
cargo test test_destroy_duplex_stream_after_unplugging_a_default_input_device -- --ignored --nocapture
|
||||
# FIXIT: The following test will hang since we don't monitor the alive status of the output device
|
||||
# cargo test test_destroy_duplex_stream_after_unplugging_a_nondefault_output_device -- --ignored --nocapture
|
||||
cargo test test_destroy_duplex_stream_after_unplugging_a_default_output_device -- --ignored --nocapture
|
||||
|
||||
cargo test test_reinit_input_stream_by_unplugging_a_nondefault_input_device -- --ignored --nocapture
|
||||
cargo test test_reinit_input_stream_by_unplugging_a_default_input_device -- --ignored --nocapture
|
||||
# FIXIT: The following test will hang since we don't monitor the alive status of the output device
|
||||
# cargo test test_reinit_output_stream_by_unplugging_a_nondefault_output_device -- --ignored --nocapture
|
||||
cargo test test_reinit_output_stream_by_unplugging_a_default_output_device -- --ignored --nocapture
|
||||
cargo test test_reinit_duplex_stream_by_unplugging_a_nondefault_input_device -- --ignored --nocapture
|
||||
cargo test test_reinit_duplex_stream_by_unplugging_a_default_input_device -- --ignored --nocapture
|
||||
# FIXIT: The following test will hang since we don't monitor the alive status of the output device
|
||||
# cargo test test_reinit_duplex_stream_by_unplugging_a_nondefault_output_device -- --ignored --nocapture
|
||||
cargo test test_reinit_duplex_stream_by_unplugging_a_default_output_device -- --ignored --nocapture
|
||||
sh run_device_tests.sh
|
||||
|
||||
# Manual Tests
|
||||
# cargo test test_switch_output_device -- --ignored --nocapture
|
||||
|
|
|
@ -1597,8 +1597,8 @@ fn is_aggregate_device(device_info: &ffi::cubeb_device_info) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
fn audiounit_device_destroy(device: &mut ffi::cubeb_device_info) {
|
||||
// This should be mapped to the memory allocation in audiounit_create_device_from_hwdev.
|
||||
fn destroy_cubeb_device_info(device: &mut ffi::cubeb_device_info) {
|
||||
// This should be mapped to the memory allocation in create_cubeb_device_info.
|
||||
// Set the pointers to null in case it points to some released memory.
|
||||
unsafe {
|
||||
if !device.device_id.is_null() {
|
||||
|
@ -1696,12 +1696,12 @@ extern "C" fn audiounit_collection_changed_callback(
|
|||
) -> OSStatus {
|
||||
let context = unsafe { &mut *(in_client_data as *mut AudioUnitContext) };
|
||||
|
||||
let queue = context.serial_queue;
|
||||
let queue = context.serial_queue.clone();
|
||||
let mutexed_context = Arc::new(Mutex::new(context));
|
||||
let also_mutexed_context = Arc::clone(&mutexed_context);
|
||||
|
||||
// This can be called from inside an AudioUnit function, dispatch to another queue.
|
||||
async_dispatch(queue, move || {
|
||||
queue.run_async(move || {
|
||||
let ctx_guard = also_mutexed_context.lock().unwrap();
|
||||
let ctx_ptr = *ctx_guard as *const AudioUnitContext;
|
||||
|
||||
|
@ -1849,10 +1849,7 @@ pub const OPS: Ops = capi_new!(AudioUnitContext, AudioUnitStream);
|
|||
#[derive(Debug)]
|
||||
pub struct AudioUnitContext {
|
||||
_ops: *const Ops,
|
||||
// serial_queue will be created by dispatch_queue_create(create_dispatch_queue)
|
||||
// without ARC(Automatic Reference Counting) support, so it should be released
|
||||
// by dispatch_release(release_dispatch_queue).
|
||||
serial_queue: dispatch_queue_t,
|
||||
serial_queue: Queue,
|
||||
latency_controller: Mutex<LatencyController>,
|
||||
devices: Mutex<SharedDevices>,
|
||||
}
|
||||
|
@ -1861,7 +1858,7 @@ impl AudioUnitContext {
|
|||
fn new() -> Self {
|
||||
Self {
|
||||
_ops: &OPS as *const _,
|
||||
serial_queue: create_dispatch_queue(DISPATCH_QUEUE_LABEL, DISPATCH_QUEUE_SERIAL),
|
||||
serial_queue: Queue::new(DISPATCH_QUEUE_LABEL),
|
||||
latency_controller: Mutex::new(LatencyController::default()),
|
||||
devices: Mutex::new(SharedDevices::default()),
|
||||
}
|
||||
|
@ -2098,7 +2095,7 @@ impl ContextOps for AudioUnitContext {
|
|||
|
||||
let mut devices = retake_forgotten_vec(coll.device, coll.count);
|
||||
for device in &mut devices {
|
||||
audiounit_device_destroy(device);
|
||||
destroy_cubeb_device_info(device);
|
||||
}
|
||||
drop(devices); // Release the memory.
|
||||
coll.device = ptr::null_mut();
|
||||
|
@ -2168,6 +2165,10 @@ impl ContextOps for AudioUnitContext {
|
|||
global_latency_frames,
|
||||
));
|
||||
|
||||
// Rename the task queue to be an unique label.
|
||||
let queue_label = format!("{}.{:p}", DISPATCH_QUEUE_LABEL, boxed_stream.as_ref());
|
||||
boxed_stream.queue = Queue::new(queue_label.as_str());
|
||||
|
||||
boxed_stream.core_stream_data =
|
||||
CoreStreamData::new(boxed_stream.as_ref(), in_stm_settings, out_stm_settings);
|
||||
|
||||
|
@ -2218,11 +2219,14 @@ impl Drop for AudioUnitContext {
|
|||
}
|
||||
}
|
||||
|
||||
// Unregister the callback if necessary.
|
||||
self.remove_devices_changed_listener(DeviceType::INPUT);
|
||||
self.remove_devices_changed_listener(DeviceType::OUTPUT);
|
||||
|
||||
release_dispatch_queue(self.serial_queue);
|
||||
// Make sure all the pending (device-collection-changed-callback) tasks
|
||||
// in queue are done, and cancel all the tasks appended after `drop` is executed.
|
||||
let queue = self.serial_queue.clone();
|
||||
queue.run_final(|| {
|
||||
// Unregister the callback if necessary.
|
||||
self.remove_devices_changed_listener(DeviceType::INPUT);
|
||||
self.remove_devices_changed_listener(DeviceType::OUTPUT);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3071,6 +3075,8 @@ impl<'ctx> Drop for CoreStreamData<'ctx> {
|
|||
struct AudioUnitStream<'ctx> {
|
||||
context: &'ctx mut AudioUnitContext,
|
||||
user_ptr: *mut c_void,
|
||||
// Task queue for the stream.
|
||||
queue: Queue,
|
||||
|
||||
data_callback: ffi::cubeb_data_callback,
|
||||
state_callback: ffi::cubeb_state_callback,
|
||||
|
@ -3107,6 +3113,7 @@ impl<'ctx> AudioUnitStream<'ctx> {
|
|||
AudioUnitStream {
|
||||
context,
|
||||
user_ptr,
|
||||
queue: Queue::new(DISPATCH_QUEUE_LABEL),
|
||||
data_callback,
|
||||
state_callback,
|
||||
device_changed_callback: Mutex::new(None),
|
||||
|
@ -3266,12 +3273,12 @@ impl<'ctx> AudioUnitStream<'ctx> {
|
|||
return;
|
||||
}
|
||||
|
||||
let queue = self.context.serial_queue;
|
||||
let queue = self.queue.clone();
|
||||
let mutexed_stm = Arc::new(Mutex::new(self));
|
||||
let also_mutexed_stm = Arc::clone(&mutexed_stm);
|
||||
// Use a new thread, through the queue, to avoid deadlock when calling
|
||||
// Get/SetProperties method from inside notify callback
|
||||
async_dispatch(queue, move || {
|
||||
queue.run_async(move || {
|
||||
let mut stm_guard = also_mutexed_stm.lock().unwrap();
|
||||
let stm_ptr = *stm_guard as *const AudioUnitStream;
|
||||
if stm_guard.destroy_pending.load(Ordering::SeqCst) {
|
||||
|
@ -3327,12 +3334,12 @@ impl<'ctx> AudioUnitStream<'ctx> {
|
|||
// Execute the stream destroy work.
|
||||
self.destroy_pending.store(true, Ordering::SeqCst);
|
||||
|
||||
let queue = self.context.serial_queue;
|
||||
let queue = self.queue.clone();
|
||||
|
||||
let stream_ptr = self as *const AudioUnitStream;
|
||||
// Execute close in serial queue to avoid collision
|
||||
// with reinit when un/plug devices
|
||||
sync_dispatch(queue, move || {
|
||||
queue.run_final(move || {
|
||||
// Call stop_audiounits to avoid potential data race. If there is a running data callback,
|
||||
// which locks a mutex inside CoreAudio framework, then this call will block the current
|
||||
// thread until the callback is finished since this call asks to lock a mutex inside
|
||||
|
@ -3361,11 +3368,10 @@ impl<'ctx> StreamOps for AudioUnitStream<'ctx> {
|
|||
self.draining.store(false, Ordering::SeqCst);
|
||||
|
||||
// Execute start in serial queue to avoid racing with destroy or reinit.
|
||||
let queue = self.context.serial_queue;
|
||||
let mut result = Err(Error::error());
|
||||
let started = &mut result;
|
||||
let stream = &self;
|
||||
sync_dispatch(queue, move || {
|
||||
self.queue.run_sync(move || {
|
||||
*started = stream.core_stream_data.start_audiounits();
|
||||
});
|
||||
|
||||
|
@ -3385,9 +3391,8 @@ impl<'ctx> StreamOps for AudioUnitStream<'ctx> {
|
|||
self.shutdown.store(true, Ordering::SeqCst);
|
||||
|
||||
// Execute stop in serial queue to avoid racing with destroy or reinit.
|
||||
let queue = self.context.serial_queue;
|
||||
let stream = &self;
|
||||
sync_dispatch(queue, move || {
|
||||
self.queue.run_sync(move || {
|
||||
stream.core_stream_data.stop_audiounits();
|
||||
});
|
||||
|
||||
|
|
|
@ -1355,24 +1355,22 @@ fn test_create_cubeb_device_info() {
|
|||
let mut results = test_create_device_infos_by_device(device);
|
||||
assert_eq!(results.len(), 2);
|
||||
// Input device type:
|
||||
let input_result = results.pop_front().unwrap();
|
||||
if is_input {
|
||||
check_device_info_by_device(
|
||||
results.pop_front().unwrap().unwrap(),
|
||||
device,
|
||||
Scope::Input,
|
||||
);
|
||||
let mut input_device_info = input_result.unwrap();
|
||||
check_device_info_by_device(&input_device_info, device, Scope::Input);
|
||||
destroy_cubeb_device_info(&mut input_device_info);
|
||||
} else {
|
||||
assert_eq!(results.pop_front().unwrap().unwrap_err(), Error::error());
|
||||
assert_eq!(input_result.unwrap_err(), Error::error());
|
||||
}
|
||||
// Output device type:
|
||||
let output_result = results.pop_front().unwrap();
|
||||
if is_output {
|
||||
check_device_info_by_device(
|
||||
results.pop_front().unwrap().unwrap(),
|
||||
device,
|
||||
Scope::Output,
|
||||
);
|
||||
let mut output_device_info = output_result.unwrap();
|
||||
check_device_info_by_device(&output_device_info, device, Scope::Output);
|
||||
destroy_cubeb_device_info(&mut output_device_info);
|
||||
} else {
|
||||
assert_eq!(results.pop_front().unwrap().unwrap_err(), Error::error());
|
||||
assert_eq!(output_result.unwrap_err(), Error::error());
|
||||
}
|
||||
} else {
|
||||
println!("No device for {:?}.", scope);
|
||||
|
@ -1390,7 +1388,7 @@ fn test_create_cubeb_device_info() {
|
|||
results
|
||||
}
|
||||
|
||||
fn check_device_info_by_device(info: ffi::cubeb_device_info, id: AudioObjectID, scope: Scope) {
|
||||
fn check_device_info_by_device(info: &ffi::cubeb_device_info, id: AudioObjectID, scope: Scope) {
|
||||
assert!(!info.devid.is_null());
|
||||
assert!(mem::size_of_val(&info.devid) >= mem::size_of::<AudioObjectID>());
|
||||
assert_eq!(info.devid as AudioObjectID, id);
|
||||
|
@ -1486,7 +1484,7 @@ fn test_is_aggregate_device() {
|
|||
assert!(!is_aggregate_device(&info));
|
||||
}
|
||||
|
||||
// device_destroy
|
||||
// destroy_cubeb_device_info
|
||||
// ------------------------------------
|
||||
#[test]
|
||||
fn test_device_destroy() {
|
||||
|
@ -1502,7 +1500,7 @@ fn test_device_destroy() {
|
|||
device.friendly_name = friendly_name.into_raw();
|
||||
device.vendor_name = vendor_name.into_raw();
|
||||
|
||||
audiounit_device_destroy(&mut device);
|
||||
destroy_cubeb_device_info(&mut device);
|
||||
|
||||
assert!(device.device_id.is_null());
|
||||
assert!(device.group_id.is_null());
|
||||
|
@ -1514,24 +1512,9 @@ fn test_device_destroy() {
|
|||
#[should_panic]
|
||||
fn test_device_destroy_with_different_device_id_and_group_id() {
|
||||
let mut device = ffi::cubeb_device_info::default();
|
||||
|
||||
let device_id = CString::new("test: device id").unwrap();
|
||||
let group_id = CString::new("test: group id").unwrap();
|
||||
let friendly_name = CString::new("test: friendly name").unwrap();
|
||||
let vendor_name = CString::new("test: vendor name").unwrap();
|
||||
|
||||
device.device_id = device_id.into_raw();
|
||||
device.group_id = group_id.into_raw();
|
||||
device.friendly_name = friendly_name.into_raw();
|
||||
device.vendor_name = vendor_name.into_raw();
|
||||
|
||||
audiounit_device_destroy(&mut device);
|
||||
// Hit the assertion above, so we will leak some memory allocated for the above cstring.
|
||||
|
||||
assert!(device.device_id.is_null());
|
||||
assert!(device.group_id.is_null());
|
||||
assert!(device.friendly_name.is_null());
|
||||
assert!(device.vendor_name.is_null());
|
||||
device.device_id = 0xdeaddead as *const _;
|
||||
device.group_id = 0xdeadbeef as *const _;
|
||||
destroy_cubeb_device_info(&mut device);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1543,7 +1526,7 @@ fn test_device_destroy_empty_device() {
|
|||
assert!(device.friendly_name.is_null());
|
||||
assert!(device.vendor_name.is_null());
|
||||
|
||||
audiounit_device_destroy(&mut device);
|
||||
destroy_cubeb_device_info(&mut device);
|
||||
|
||||
assert!(device.device_id.is_null());
|
||||
assert!(device.group_id.is_null());
|
||||
|
|
|
@ -32,18 +32,18 @@ fn test_switch_device() {
|
|||
}
|
||||
|
||||
fn test_switch_device_in_scope(scope: Scope) {
|
||||
// Do nothing if there is no 2 available devices at least.
|
||||
let devices = test_get_devices_in_scope(scope.clone());
|
||||
if devices.len() < 2 {
|
||||
println!("Need 2 devices for {:?} at least.", scope);
|
||||
return;
|
||||
}
|
||||
|
||||
println!(
|
||||
"Switch default device for {:?} while the stream is working.",
|
||||
scope
|
||||
);
|
||||
|
||||
// Do nothing if there is no 2 available devices at least.
|
||||
let devices = test_get_devices_in_scope(scope.clone());
|
||||
if devices.len() < 2 {
|
||||
println!("Need 2 devices for {:?} at least. Skip.", scope);
|
||||
return;
|
||||
}
|
||||
|
||||
let mut device_switcher = TestDeviceSwitcher::new(scope.clone());
|
||||
|
||||
let count = Arc::new(Mutex::new(0));
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -0,0 +1,101 @@
|
|||
language: rust
|
||||
# sudo is required to enable kcov to use the personality syscall
|
||||
sudo: required
|
||||
dist: trusty
|
||||
cache: cargo
|
||||
|
||||
rust:
|
||||
- nightly
|
||||
- beta
|
||||
- stable
|
||||
- 1.31.0
|
||||
|
||||
env:
|
||||
matrix:
|
||||
- FEATURES='--features "regexp regexp_macros"'
|
||||
|
||||
before_script:
|
||||
- eval git pull --rebase https://github.com/Geal/nom master
|
||||
- eval git log --pretty=oneline HEAD~5..HEAD
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- rust: nightly
|
||||
env: FEATURES='--no-default-features'
|
||||
- rust: nightly
|
||||
env: FEATURES='--no-default-features --features "alloc"'
|
||||
- rust: stable
|
||||
env: FEATURES=''
|
||||
- rust: nightly
|
||||
env: DOC_FEATURES='--features "std lexical regexp regexp_macros" --no-default-features'
|
||||
before_script:
|
||||
- export PATH=$HOME/.cargo/bin:$PATH
|
||||
script:
|
||||
- eval cargo doc --verbose $DOC_FEATURES
|
||||
- rust: nightly
|
||||
env: FEATURES=''
|
||||
before_script:
|
||||
- export PATH=$HOME/.cargo/bin:$PATH
|
||||
- cargo install cargo-update || echo "cargo-update already installed"
|
||||
- cargo install cargo-travis || echo "cargo-travis already installed"
|
||||
- cargo install-update -a
|
||||
- mkdir -p target/kcov-master
|
||||
script:
|
||||
cargo coveralls --verbose --all-features
|
||||
allow_failures:
|
||||
- rust: stable
|
||||
env: FEATURES=''
|
||||
before_script:
|
||||
- export PATH=$HOME/.cargo/bin:$PATH
|
||||
- rustup component add rustfmt-preview
|
||||
script:
|
||||
- eval cargo fmt -- --write-mode=diff
|
||||
|
||||
notifications:
|
||||
webhooks:
|
||||
urls:
|
||||
- https://webhooks.gitter.im/e/9c035a194ac4fd4cc061
|
||||
on_success: change
|
||||
on_failure: always
|
||||
on_start: false
|
||||
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libcurl4-openssl-dev
|
||||
- libelf-dev
|
||||
- libdw-dev
|
||||
- binutils-dev
|
||||
- cmake
|
||||
sources:
|
||||
- kalakris-cmake
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- /home/travis/.cargo
|
||||
|
||||
before_cache:
|
||||
- rm -rf /home/travis/.cargo/registry
|
||||
|
||||
script:
|
||||
- eval cargo build --verbose $FEATURES
|
||||
- eval cargo test --verbose $FEATURES
|
||||
|
||||
after_success: |
|
||||
case "$TRAVIS_RUST_VERSION" in
|
||||
nightly)
|
||||
if [ "${TRAVIS_PULL_REQUEST_BRANCH:-$TRAVIS_BRANCH}" != "master" ]; then
|
||||
git fetch &&
|
||||
git checkout master &&
|
||||
cargo bench --verbose
|
||||
fi
|
||||
|
||||
if [ "$FEATURES" == '--features "regexp regexp_macros"' ]; then
|
||||
cargo bench --verbose
|
||||
fi
|
||||
;;
|
||||
|
||||
*)
|
||||
;;
|
||||
esac
|
|
@ -19,7 +19,7 @@ static_prefs = { path = "../../../../modules/libpref/init/static_prefs" }
|
|||
profiler_helper = { path = "../../../../tools/profiler/rust-helper", optional = true }
|
||||
mozurl = { path = "../../../../netwerk/base/mozurl" }
|
||||
webrender_bindings = { path = "../../../../gfx/webrender_bindings", optional = true }
|
||||
cubeb-coreaudio = { git = "https://github.com/ChunMinChang/cubeb-coreaudio-rs", rev = "4acd80233efa645ac79769f37b07d495c1b42070", optional = true }
|
||||
cubeb-coreaudio = { git = "https://github.com/ChunMinChang/cubeb-coreaudio-rs", rev = "799518a033a0c780cfdb82a4eaabfd9681cb7f3b", optional = true }
|
||||
cubeb-pulse = { git = "https://github.com/djg/cubeb-pulse-rs", rev="8069f8f4189982e0b38fa6dc8993dd4fab41f728", optional = true, features=["pulse-dlopen"] }
|
||||
cubeb-sys = { version = "0.6", optional = true, features=["gecko-in-tree"] }
|
||||
encoding_glue = { path = "../../../../intl/encoding_glue" }
|
||||
|
|
Загрузка…
Ссылка в новой задаче