/* * Copyright 2015 Joshua R. Rodgers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #![allow(dead_code)] #[macro_use] extern crate dx_core; use std::thread; use std::borrow::Borrow; use std::sync::mpsc::channel; use dx_core::com::{IID, ComRef, Unknown, IUnknown}; struct TestVtable { query_interface: extern "stdcall" fn(*mut Test, *const IID, *mut *mut ()) -> i32, add_ref: extern "stdcall" fn(*mut Test) -> u32, release: extern "stdcall" fn(*mut Test) -> u32 } struct Test { vtable: *mut TestVtable, ref_count: u32 } impl_iid! { Test : 0x7B7166EC, 0x21C7, 0x44AE, 0xB2, 0x1A, 0xC9, 0xAE, 0x32, 0x1A, 0xE3, 0x69 } extern "stdcall" fn query_interface(this: *mut Test, _: *const IID, interface: *mut *mut ()) -> i32 { unsafe { (*this).ref_count += 1; *interface = ::std::mem::transmute(this); } 0 } extern "stdcall" fn add_ref(this: *mut Test) -> u32 { unsafe { (*this).ref_count += 1; (*this).ref_count } } extern "stdcall" fn release(this: *mut Test) -> u32 { unsafe { (*this).ref_count -= 1; let ref_count = (*this).ref_count; //Be nice and cleanup after yourself! :) if ref_count == 0 { ::std::mem::drop(::std::mem::transmute::<_, Box>((*this).vtable)); ::std::mem::drop(::std::mem::transmute::<_, Box>(this)); } ref_count } } fn get_test_ptr() -> *mut Unknown { unsafe { let vtable = Box::new(TestVtable { query_interface: query_interface, add_ref: add_ref, release: release }); let test = Box::new(Test { vtable: ::std::mem::transmute(vtable), ref_count: 1 }); ::std::mem::transmute(test) } } #[test] fn query_interface_should_return_ok_for_unknown_iid() { let factory = ComRef::new(get_test_ptr()); let result = factory.query_interface::(); match result { Ok(_) => (), Err(_) => panic!() } } #[test] fn add_ref_returns_the_expected_number_of_references() { let factory = get_test_ptr(); let actual = unsafe { let actual = (*factory).add_ref(); (*factory).release(); (*factory).release(); actual }; assert_eq!(2, actual); } #[test] fn release_returns_the_expected_number_of_references() { let factory = get_test_ptr(); let actual = unsafe { (*factory).release() }; assert_eq!(0, actual); } #[test] fn comref_can_be_sent_over_channels() { let expected_ref = ComRef::new(get_test_ptr()); let (tx, rx) = channel::>(); tx.send(expected_ref.clone()).unwrap(); thread::spawn(move || { let actual_ref = rx.recv().unwrap(); assert_eq!((&*actual_ref as *const Unknown), (&*expected_ref as *const Unknown)); }).join().unwrap(); } #[test] fn comref_can_be_borrowed() { let expected_ref = ComRef::new(get_test_ptr()); let borrowed_ref: &Unknown = expected_ref.borrow(); assert_eq!((&*expected_ref as *const Unknown), (borrowed_ref as *const Unknown)) }