Files
core-foundation-rs/cocoa/tests/foundation.rs
T
Steven Sheldon 0a1b0d6f1b Fixes so the compiler can infer msg_send! return types
Currently, due to a quirk in Rust's type inference interacting with the
structure of the msg_send! macro, a () return type will be inferred when
the compiler cannot otherwise determine the return type. This behavior
is expected to change, and in the future could resolve to a ! return
type, which results in undefined behavior.

Linting has previously been added for this in rust-lang/rust#39216, but
it did not catch these cases due to SSheldon/rust-objc#62. An upcoming
version of objc will be fixed to stop hiding these errors, at which
point they will become compile errors.

This change fixes these errors and allows cocoa to compile with the
fixed version of objc.
2019-10-16 06:50:58 -07:00

196 lines
7.7 KiB
Rust

#[macro_use]
extern crate objc;
extern crate block;
extern crate cocoa;
#[cfg(test)]
mod foundation {
mod nsstring {
use cocoa::foundation::NSString;
use cocoa::base::nil;
use std::slice;
use std::str;
#[test]
fn test_utf8() {
let expected = "Iñtërnâtiônàlizætiøn";
unsafe {
let built = NSString::alloc(nil).init_str(expected);
let bytes = built.UTF8String() as *const u8;
let objc_string = str::from_utf8(slice::from_raw_parts(bytes, built.len()))
.unwrap();
assert_eq!(objc_string.len(), expected.len());
assert_eq!(objc_string, expected);
}
}
#[test]
fn test_string() {
let expected = "Hello World!";
unsafe {
let built = NSString::alloc(nil).init_str(expected);
let bytes = built.UTF8String() as *const u8;
let objc_string = str::from_utf8(slice::from_raw_parts(bytes, built.len()))
.unwrap();
assert_eq!(objc_string.len(), expected.len());
assert_eq!(objc_string, expected);
}
}
#[test]
fn test_length() {
let expected = "Hello!";
unsafe {
let built = NSString::alloc(nil).init_str(expected);
assert_eq!(built.len(), expected.len());
}
}
#[test]
fn test_append_by_appending_string() {
let initial_str = "Iñtërnâtiônàlizætiøn";
let to_append = "_more_strings";
let expected = concat!("Iñtërnâtiônàlizætiøn", "_more_strings");
unsafe {
let built = NSString::alloc(nil).init_str(initial_str);
let built_to_append = NSString::alloc(nil).init_str(to_append);
let append_string = built.stringByAppendingString_(built_to_append);
let bytes = append_string.UTF8String() as *const u8;
let objc_string = str::from_utf8(slice::from_raw_parts(bytes, append_string.len()))
.unwrap();
assert_eq!(objc_string, expected);
}
}
}
mod nsfastenumeration {
use std::str;
use std::slice;
use cocoa::foundation::{NSString, NSFastEnumeration};
use cocoa::base::{id, nil};
#[test]
fn test_iter() {
unsafe {
let string = NSString::alloc(nil).init_str("this is a test string");
let separator = NSString::alloc(nil).init_str(" ");
let components: id = msg_send![string, componentsSeparatedByString: separator];
let combined = components.iter()
.map(|s| {
let bytes = s.UTF8String() as *const u8;
str::from_utf8(slice::from_raw_parts(bytes, s.len())).unwrap()
})
.fold(String::new(), |mut acc, s| {
acc.push_str(s);
acc
});
assert_eq!(combined, "thisisateststring");
}
}
#[test]
#[should_panic]
fn test_mutation() {
unsafe {
let string = NSString::alloc(nil).init_str("this is a test string");
let separator = NSString::alloc(nil).init_str(" ");
let components: id = msg_send![string, componentsSeparatedByString: separator];
let mut_components: id = msg_send![components, mutableCopy];
let mut iter = mut_components.iter();
iter.next();
let () = msg_send![mut_components, removeObjectAtIndex:1];
iter.next();
}
}
}
mod nsdictionary {
use block::ConcreteBlock;
use cocoa::foundation::{NSArray, NSComparisonResult, NSDictionary, NSFastEnumeration,
NSString};
use cocoa::base::{id, nil};
#[test]
fn test_get() {
const KEY: &'static str = "The key";
const VALUE: &'static str = "Some value";
unsafe {
let key = NSString::alloc(nil).init_str(KEY);
let value = NSString::alloc(nil).init_str(VALUE);
let dict = NSDictionary::dictionaryWithObject_forKey_(nil, value, key);
let retrieved_value = dict.objectForKey_(key);
assert!(retrieved_value.isEqualToString(VALUE));
}
}
#[test]
fn test_iter() {
let mkstr = |s| unsafe { NSString::alloc(nil).init_str(s) };
let keys = vec!["a", "b", "c", "d", "e", "f"];
let objects = vec!["1", "2", "3", "4", "5", "6"];
unsafe {
use std::{slice, str};
use std::cmp::{Ord, Ordering};
let keys_raw_vec = keys.clone().into_iter().map(&mkstr).collect::<Vec<_>>();
let objs_raw_vec = objects.clone().into_iter().map(&mkstr).collect::<Vec<_>>();
let keys_array = NSArray::arrayWithObjects(nil, &keys_raw_vec);
let objs_array = NSArray::arrayWithObjects(nil, &objs_raw_vec);
let dict =
NSDictionary::dictionaryWithObjects_forKeys_(nil, objs_array, keys_array);
// NSDictionary does not store its contents in order of insertion, so ask for
// sorted iterators to ensure that each item is the same as its counterpart in
// the vector.
fn compare_function(s0: &id, s1: &id) -> Ordering {
unsafe {
let (bytes0, len0) = (s0.UTF8String() as *const u8, s0.len());
let (bytes1, len1) = (s1.UTF8String() as *const u8, s1.len());
let (s0, s1) = (str::from_utf8(slice::from_raw_parts(bytes0, len0)).unwrap(),
str::from_utf8(slice::from_raw_parts(bytes1, len1)).unwrap());
let (c0, c1) = (s0.chars().next().unwrap(), s1.chars().next().unwrap());
c0.cmp(&c1)
}
}
// First test cocoa sorting...
let mut comparator = ConcreteBlock::new(|s0: id, s1: id| {
match compare_function(&s0, &s1) {
Ordering::Less => NSComparisonResult::NSOrderedAscending,
Ordering::Equal => NSComparisonResult::NSOrderedSame,
Ordering::Greater => NSComparisonResult::NSOrderedDescending,
}
});
let associated_iter = keys.iter().zip(objects.iter());
for (k_id, (k, v)) in dict.keysSortedByValueUsingComparator_(&mut *comparator)
.iter()
.zip(associated_iter) {
assert!(k_id.isEqualToString(k));
let v_id = dict.objectForKey_(k_id);
assert!(v_id.isEqualToString(v));
}
// Then use rust sorting
let mut keys_arr = dict.allKeys().iter().collect::<Vec<_>>();
keys_arr.sort_by(compare_function);
for (k0, k1) in keys_arr.into_iter().zip(keys.iter()) {
assert!(k0.isEqualToString(k1));
}
let mut objects_arr = dict.allValues().iter().collect::<Vec<_>>();
objects_arr.sort_by(compare_function);
for (v0, v1) in objects_arr.into_iter().zip(objects.iter()) {
assert!(v0.isEqualToString(v1));
}
}
}
}
}