Skip to content

Commit 7390ebe

Browse files
authored
Add tests for macos libraries (#1347)
2 parents 5e6babe + 83d4b5e commit 7390ebe

9 files changed

Lines changed: 147 additions & 14 deletions

File tree

crates/mac_core/src/array.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,34 @@ impl<T: CoreType> CFArray<T> {
7171
unsafe { T::new_borrowed(ptr.cast()) }
7272
}
7373
}
74+
75+
#[cfg(test)]
76+
mod tests {
77+
use super::*;
78+
use crate::CFString;
79+
80+
#[test]
81+
fn test_array_index() {
82+
let foo = CFString::new("foo");
83+
let bar = CFString::new("bar");
84+
85+
let arr = CFArray::new(&[foo.clone(), bar.clone()]);
86+
87+
assert_eq!(arr.get(0).as_type_ref(), foo.as_type_ref());
88+
assert_eq!(arr.get(1).as_type_ref(), bar.as_type_ref());
89+
assert_eq!(arr.get(0).as_str(), "foo");
90+
assert_eq!(arr.get(1).as_str(), "bar");
91+
}
92+
93+
#[test]
94+
fn test_array_len() {
95+
let empty = CFArray::<CFString>::empty();
96+
let two = CFArray::new(&[CFString::new("foo"), CFString::new("bar")]);
97+
98+
assert!(empty.is_empty());
99+
assert_eq!(empty.len(), 0);
100+
101+
assert!(!two.is_empty());
102+
assert_eq!(two.len(), 2);
103+
}
104+
}

crates/mac_core/src/dict.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ cfty! {
7575
}
7676

7777
impl<K: CoreType, V: CoreType> CFDictionary<K, V> {
78+
// TODO: Bind the keys/values of P to K and V
7879
/// Create a new [`CFDictionary`] that contains the provided key/value pairs.
7980
pub fn new<P: Pairs>(pairs: P) -> CFDictionary<K, V> {
8081
let (keys, values) = pairs.into_pairs();
@@ -96,3 +97,31 @@ impl<K: CoreType, V: CoreType> CFDictionary<K, V> {
9697
unsafe { CFDictionary::new_owned(ptr) }
9798
}
9899
}
100+
101+
#[cfg(test)]
102+
mod tests {
103+
use super::*;
104+
use crate::CFString;
105+
106+
#[test]
107+
fn test_new_arr() {
108+
let one = CFString::new("1");
109+
let two = CFString::new("2");
110+
let three = CFString::new("3");
111+
let four = CFString::new("4");
112+
113+
let _ = CFDictionary::<CFString, CFString>::new::<[(CFString, CFString); 0]>([]);
114+
let _ = CFDictionary::<CFString, CFString>::new([(one, two), (three, four)]);
115+
}
116+
117+
#[test]
118+
fn test_new_vec() {
119+
let one = CFString::new("1");
120+
let two = CFString::new("2");
121+
let three = CFString::new("3");
122+
let four = CFString::new("4");
123+
124+
let _ = CFDictionary::<CFString, CFString>::new::<Vec<(CFString, CFString)>>(vec![]);
125+
let _ = CFDictionary::<CFString, CFString>::new(vec![(one, two), (three, four)]);
126+
}
127+
}

crates/mac_core/src/lib.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,4 +167,24 @@ mod tests {
167167
assert_eq!(CFTypeId::of_val(&ty), CFTypeId::of::<CFArray<CFType>>());
168168
drop(ty);
169169
}
170+
171+
#[test]
172+
fn test_downcast() {
173+
let arr = CFArray::<CFType>::empty();
174+
let ty = arr.into_ty();
175+
176+
assert!(unsafe { ty.downcast::<CFArray<CFType>>() }.is_ok());
177+
178+
let arr = CFArray::<CFType>::empty();
179+
let ty = arr.into_ty();
180+
assert!(unsafe { ty.downcast::<CFString>() }.is_err());
181+
182+
let str = CFString::new("");
183+
let ty = str.into_ty();
184+
assert!(unsafe { ty.downcast::<CFString>() }.is_ok());
185+
186+
let str = CFString::new("");
187+
let ty = str.into_ty();
188+
assert!(unsafe { ty.downcast::<CFArray<CFString>>() }.is_err());
189+
}
170190
}

crates/mac_core/src/set.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,16 @@ impl<T: CoreType> CFSet<T> {
2626
unsafe { CFSet::new_owned(ptr) }
2727
}
2828
}
29+
30+
#[cfg(test)]
31+
mod tests {
32+
use super::*;
33+
use crate::CFString;
34+
35+
#[test]
36+
fn test_new() {
37+
// Ensure empty and with-value set construction don't segfault
38+
let _ = CFSet::<CFString>::new(&[]);
39+
let _ = CFSet::new(&[CFString::new("foo")]);
40+
}
41+
}

crates/mac_core/src/string.rs

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ impl StrLike for CStr {
2929
if self.to_str().is_ok() {
3030
sys::kCFStringEncodingUTF8
3131
} else {
32-
sys::kCFStringEncodingNonLossyASCII
32+
sys::kCFStringEncodingASCII
3333
}
3434
}
3535

@@ -139,7 +139,7 @@ impl CFString {
139139
}
140140

141141
/// Get this value as a null-terminated C-string
142-
pub fn get_cstring(&self) -> CString {
142+
pub fn get_cstring(&self) -> Option<CString> {
143143
let len = self.len() * 4 + 1;
144144
let mut buf = vec![0; len];
145145
// SAFETY: Self is guaranteed valid. Buffer is definitely of sufficient length to hold the
@@ -154,24 +154,61 @@ impl CFString {
154154
};
155155
if res {
156156
let buf = buf.into_iter().take_while(|&c| c != 0).collect::<Vec<_>>();
157-
CString::new(buf).unwrap()
157+
if buf.len() != self.len() {
158+
None
159+
} else {
160+
Some(CString::new(buf).unwrap())
161+
}
158162
} else {
159-
panic!("Invalid C String")
163+
None
160164
}
161165
}
162166

163167
/// Attempt to get a reference to this value as a `CStr`, if the value is natively UTF-8,
164168
/// otherwise allocate a CString in that encoding.
165-
pub fn as_cstr(&self) -> Cow<'_, CStr> {
169+
pub fn as_cstr(&self) -> Option<Cow<'_, CStr>> {
166170
// SAFETY: Self is guaranteed valid
167171
let cstr =
168172
unsafe { sys::CFStringGetCStringPtr(self.as_type_ref(), sys::kCFStringEncodingUTF8) };
169173
if cstr.is_null() {
170-
Cow::Owned(self.get_cstring())
174+
self.get_cstring().map(Cow::Owned)
171175
} else {
172176
// SAFETY: If non-null, the return value of CFStringGetCStringPtr is guaranteed to be a
173177
// valid C-string
174-
Cow::Borrowed(unsafe { CStr::from_ptr(cstr) })
178+
Some(Cow::Borrowed(unsafe { CStr::from_ptr(cstr) }))
175179
}
176180
}
177181
}
182+
183+
#[cfg(test)]
184+
mod tests {
185+
use super::*;
186+
187+
#[test]
188+
fn test_as_str() {
189+
let str = CFString::new("foo");
190+
assert_eq!(str.as_str(), Cow::Borrowed("foo"));
191+
192+
let cstr = CFString::new(c"foo");
193+
assert_eq!(cstr.as_str(), Cow::Borrowed("foo"));
194+
195+
// Invalid UTF-8 CStrings are treated as extended-ASCII.
196+
let non_str = CFString::new(c"\xC3\x28bar");
197+
assert_eq!(non_str.as_str(), Cow::<str>::Owned("Ã(bar".to_string()));
198+
}
199+
200+
#[test]
201+
fn test_as_cstr() {
202+
let str = CFString::new("foo");
203+
assert_eq!(str.as_cstr(), Some(Cow::<CStr>::Owned(c"foo".to_owned())));
204+
205+
let cstr = CFString::new(c"foo");
206+
assert_eq!(cstr.as_cstr(), Some(Cow::Borrowed(c"foo")));
207+
}
208+
209+
#[test]
210+
fn test_as_non_cstr() {
211+
let non_cstr = CFString::new("fo\0o");
212+
assert!(non_cstr.as_cstr().is_none());
213+
}
214+
}

crates/mac_core/src/sys.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ pub const kCFStringEncodingASCII: CFStringEncoding = 0x0600;
7878
pub const kCFStringEncodingNonLossyASCII: CFStringEncoding = 0x0BFF;
7979
pub const kCFStringEncodingUnicode: CFStringEncoding = 0x0100;
8080
pub const kCFStringEncodingUTF8: CFStringEncoding = 0x08000100;
81+
pub const kCFStringEncodingInvalidId: CFStringEncoding = 0xFFFFFFFF;
8182

8283
#[link(name = "CoreFoundation", kind = "framework")]
8384
extern "C" {

crates/xetex_layout/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ tectonic_mac_core = { path = "../mac_core", version = "0.0.0-dev.0" }
4444
external-harfbuzz = ["tectonic_bridge_harfbuzz/external-harfbuzz"]
4545

4646
[package.metadata.internal_dep_versions]
47-
tectonic_mac_core = "6932d5f15fec0fb5c219b887bdd11b72641af07a"
47+
tectonic_mac_core = "thiscommit:2026-04-27:052BNSA"
4848
tectonic_bridge_core = "thiscommit:2021-01-16:wie2Ejoh"
4949
tectonic_bridge_fontconfig = "89268e8317eee0e23f9c0d572aa764d4504d85b5"
5050
tectonic_bridge_graphite2 = "2722731f9e32c6963fe8c8566a201b33672c5c5a"

crates/xetex_layout/src/font.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -581,7 +581,9 @@ pub(crate) fn get_file_name_from_ct_font(ct_font: &CTFont, index: &mut u32) -> O
581581
*index = i as u32;
582582
break;
583583
}
584-
(Some(name1), Some(name2)) if &*name1.as_cstr() == name2 => {
584+
(Some(name1), Some(name2))
585+
if name1.as_cstr().is_some_and(|name1| name1 == name2) =>
586+
{
585587
*index = i as u32;
586588
break;
587589
}

crates/xetex_layout/src/manager/mac.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,12 @@ fn find_font_with_name(name: CFString, key: FontAttribute) -> Option<CTFontDescr
3030

3131
fn append_name_to_list(font: &CTFont, name_list: &mut Vec<CString>, name_key: FontNameKey) {
3232
let name = font.name(name_key);
33-
if let Some(name) = name {
34-
FontManager::append_to_list(name_list, name.as_cstr());
33+
if let Some(name) = name.as_ref().and_then(|n| n.as_cstr()) {
34+
FontManager::append_to_list(name_list, name);
3535
}
3636
let name = font.localized_name(name_key);
37-
if let Some(name) = name {
38-
FontManager::append_to_list(name_list, name.as_cstr());
37+
if let Some(name) = name.as_ref().and_then(|n| n.as_cstr()) {
38+
FontManager::append_to_list(name_list, name);
3939
}
4040
}
4141

@@ -152,7 +152,7 @@ impl FontManagerBackend for MacBackend {
152152
// SAFETY: CFString has no generic parameters
153153
let ps_name = unsafe { ps_name.downcast::<CFString>() }.unwrap();
154154

155-
names.ps_name = Some(ps_name.get_cstring());
155+
names.ps_name = ps_name.get_cstring();
156156

157157
let font = CTFont::new_descriptor(&font, 0.0);
158158
append_name_to_list(&font, &mut names.full_names, FontNameKey::Full);

0 commit comments

Comments
 (0)