|
8 | 8 | use std::collections::HashSet;
|
9 | 9 |
|
10 | 10 | use crate::framework::{expect_debug_panic_or_release_ok, itest};
|
11 |
| -use godot::builtin::{GString, PackedStringArray}; |
| 11 | +use godot::builtin::{Encoding, GString, PackedStringArray}; |
12 | 12 |
|
13 | 13 | // TODO use tests from godot-rust/gdnative
|
14 | 14 |
|
@@ -267,6 +267,131 @@ fn string_pad() {
|
267 | 267 | }
|
268 | 268 |
|
269 | 269 | // ----------------------------------------------------------------------------------------------------------------------------------------------
|
| 270 | +// Byte and C-string conversions |
| 271 | + |
| 272 | +#[itest] |
| 273 | +fn string_from_bytes_ascii() { |
| 274 | + let ascii = GString::try_from_bytes(b"Hello", Encoding::Ascii).expect("valid ASCII"); |
| 275 | + assert_eq!(ascii, GString::from("Hello")); |
| 276 | + assert_eq!(ascii.len(), 5); |
| 277 | + |
| 278 | + let ascii_nul = GString::try_from_bytes(b"Hello\0", Encoding::Ascii); |
| 279 | + assert_eq!(ascii_nul, None, "intermediate NUL byte is not valid ASCII"); // at end, but still not NUL terminator. |
| 280 | + |
| 281 | + let latin1 = GString::try_from_bytes(b"/\xF0\xF5\xBE", Encoding::Ascii); |
| 282 | + assert_eq!(latin1, None, "Latin-1 is *not* valid ASCII"); |
| 283 | + |
| 284 | + let utf8 = GString::try_from_bytes(b"\xF6\xF0\x9F\x8D\x8E\xF0\x9F\x92\xA1", Encoding::Ascii); |
| 285 | + assert_eq!(utf8, None, "UTF-8 is *not* valid ASCII"); |
| 286 | +} |
| 287 | + |
| 288 | +#[itest] |
| 289 | +fn string_from_cstr_ascii() { |
| 290 | + let ascii = GString::try_from_cstr(c"Hello", Encoding::Ascii); |
| 291 | + let ascii = ascii.expect("valid ASCII"); |
| 292 | + assert_eq!(ascii, GString::from("Hello")); |
| 293 | + assert_eq!(ascii.len(), 5); |
| 294 | + |
| 295 | + let latin1 = GString::try_from_cstr(c"/ðõ¾", Encoding::Ascii); |
| 296 | + assert_eq!(latin1, None, "Latin-1 is *not* valid ASCII"); |
| 297 | + |
| 298 | + let utf8 = GString::try_from_cstr(c"ö🍎A💡", Encoding::Ascii); |
| 299 | + assert_eq!(utf8, None, "UTF-8 is *not* valid ASCII"); |
| 300 | +} |
| 301 | + |
| 302 | +#[itest] |
| 303 | +fn string_from_bytes_latin1() { |
| 304 | + let ascii = GString::try_from_bytes(b"Hello", Encoding::Latin1); |
| 305 | + let ascii = ascii.expect("ASCII is valid Latin-1"); |
| 306 | + assert_eq!(ascii, GString::from("Hello")); |
| 307 | + assert_eq!(ascii.len(), 5); |
| 308 | + |
| 309 | + let latin1 = GString::try_from_bytes(b"/\xF0\xF5\xBE", Encoding::Latin1); |
| 310 | + let latin1 = latin1.expect("Latin-1 is valid Latin-1"); |
| 311 | + assert_eq!(latin1, GString::from("/ðõ¾")); |
| 312 | + assert_eq!(latin1.len(), 4); |
| 313 | + |
| 314 | + let latin1_nul = GString::try_from_bytes(b"/\0\xF0\xF5\xBE", Encoding::Latin1); |
| 315 | + assert_eq!( |
| 316 | + latin1_nul, None, |
| 317 | + "intermediate NUL byte is not valid Latin-1" |
| 318 | + ); |
| 319 | + |
| 320 | + // UTF-8 -> Latin-1: always succeeds, even if result is garbage, since every byte is a valid Latin-1 character. |
| 321 | + let utf8 = GString::try_from_bytes( |
| 322 | + b"\xC3\xB6\xF0\x9F\x8D\x8E\x41\xF0\x9F\x92\xA1", |
| 323 | + Encoding::Latin1, |
| 324 | + ); |
| 325 | + let utf8 = utf8.expect("UTF-8 is valid Latin-1, even if garbage"); |
| 326 | + assert_eq!(utf8, GString::from("öðAð¡")); |
| 327 | +} |
| 328 | + |
| 329 | +#[itest] |
| 330 | +fn string_from_cstr_latin1() { |
| 331 | + let ascii = GString::try_from_cstr(c"Hello", Encoding::Latin1); |
| 332 | + let ascii = ascii.expect("ASCII is valid Latin-1"); |
| 333 | + assert_eq!(ascii, GString::from("Hello")); |
| 334 | + assert_eq!(ascii.len(), 5); |
| 335 | + |
| 336 | + // The C-string literal is interpreted as UTF-8, not Latin-1 (which is btw still valid Latin-1), see last test in this #[itest]. |
| 337 | + // So we use explicit bytes in the following tests. |
| 338 | + assert_eq!(c"/ðõ¾".to_bytes(), b"/\xC3\xB0\xC3\xB5\xC2\xBE"); |
| 339 | + let latin1 = GString::try_from_cstr(c"/\xF0\xF5\xBE", Encoding::Latin1); |
| 340 | + let latin1 = latin1.expect("Latin-1 is valid Latin-1"); |
| 341 | + assert_eq!(latin1, GString::from("/ðõ¾")); |
| 342 | + assert_eq!(latin1.len(), 4); |
| 343 | + |
| 344 | + // UTF-8 -> Latin-1: always succeeds, even if result is garbage, since every byte is a valid Latin-1 character. |
| 345 | + let utf8 = GString::try_from_cstr(c"ö🍎A💡", Encoding::Latin1); |
| 346 | + let utf8 = utf8.expect("UTF-8 is valid Latin-1, even if garbage"); |
| 347 | + assert_eq!(utf8, GString::from("öðAð¡")); |
| 348 | +} |
| 349 | + |
| 350 | +#[itest] |
| 351 | +fn string_from_bytes_utf8() { |
| 352 | + let ascii = GString::try_from_bytes(b"Hello", Encoding::Utf8); |
| 353 | + let ascii = ascii.expect("ASCII is valid UTF-8"); |
| 354 | + assert_eq!(ascii, GString::from("Hello")); |
| 355 | + assert_eq!(ascii.len(), 5); |
| 356 | + |
| 357 | + let latin1 = GString::try_from_bytes(b"/\xF0\xF5\xBE", Encoding::Utf8); |
| 358 | + assert_eq!(latin1, None, "Latin-1 is *not* valid UTF-8"); |
| 359 | + |
| 360 | + let utf8 = GString::try_from_bytes( |
| 361 | + b"\xC3\xB6\xF0\x9F\x8D\x8E\x41\xF0\x9F\x92\xA1", |
| 362 | + Encoding::Utf8, |
| 363 | + ); |
| 364 | + let utf8 = utf8.expect("UTF-8 is valid UTF-8"); |
| 365 | + assert_eq!(utf8, GString::from("ö🍎A💡")); |
| 366 | + assert_eq!(utf8.len(), 4); |
| 367 | + |
| 368 | + let utf8_nul = GString::try_from_bytes(b"\xC3\0A", Encoding::Utf8); |
| 369 | + assert_eq!(utf8_nul, None, "intermediate NUL byte is not valid UTF-8"); |
| 370 | +} |
| 371 | + |
| 372 | +#[itest] |
| 373 | +fn string_from_cstr_utf8() { |
| 374 | + let ascii = GString::try_from_cstr(c"Hello", Encoding::Utf8); |
| 375 | + let ascii = ascii.expect("ASCII is valid UTF-8"); |
| 376 | + assert_eq!(ascii, GString::from("Hello")); |
| 377 | + assert_eq!(ascii.len(), 5); |
| 378 | + |
| 379 | + // The latin1 checks pass even though try_from_bytes() for the Latin-1 string b"/\xF0\xF5\xBE" fails. |
| 380 | + // When using a C string literal, the characters are interpreted as UTF-8, *not* Latin-1, see following assertion. |
| 381 | + assert_eq!(c"/ðõ¾".to_bytes(), b"/\xC3\xB0\xC3\xB5\xC2\xBE"); |
| 382 | + let latin1 = GString::try_from_cstr(c"/ðõ¾", Encoding::Utf8); |
| 383 | + let latin1 = latin1.expect("Characters from Latin-1 set re-encoded as UTF-8 are valid UTF-8"); |
| 384 | + assert_eq!(latin1, GString::from("/ðõ¾")); |
| 385 | + assert_eq!(latin1.len(), 4); |
| 386 | + |
| 387 | + let utf8 = GString::try_from_cstr(c"ö🍎A💡", Encoding::Utf8); |
| 388 | + let utf8 = utf8.expect("valid UTF-8"); |
| 389 | + assert_eq!(utf8, GString::from("ö🍎A💡")); |
| 390 | + assert_eq!(utf8.len(), 4); |
| 391 | +} |
| 392 | + |
| 393 | +// ---------------------------------------------------------------------------------------------------------------------------------------------- |
| 394 | +// Helpers |
270 | 395 |
|
271 | 396 | fn packed(strings: &[&str]) -> PackedStringArray {
|
272 | 397 | strings.iter().map(|&s| GString::from(s)).collect()
|
|
0 commit comments