2023-01-05 13:25:55 +01:00
/**************************************************************************/
/* test_string.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
2018-01-05 00:50:27 +01:00
2014-02-10 02:10:30 +01:00
# ifndef TEST_STRING_H
# define TEST_STRING_H
2020-11-07 23:33:38 +01:00
# include "core/string/ustring.h"
2014-02-10 02:10:30 +01:00
2020-07-30 15:54:08 +02:00
# include "tests/test_macros.h"
2020-07-20 18:35:34 +02:00
2014-02-10 02:10:30 +01:00
namespace TestString {
2020-07-27 12:43:20 +02:00
int u32scmp ( const char32_t * l , const char32_t * r ) {
2022-01-27 17:34:33 +01:00
for ( ; * l = = * r & & * l & & * r ; l + + , r + + ) {
2022-02-16 13:56:32 +01:00
// Continue.
2022-01-27 17:34:33 +01:00
}
2020-07-27 12:43:20 +02:00
return * l - * r ;
}
TEST_CASE ( " [String] Assign Latin-1 char string " ) {
2020-07-20 18:35:34 +02:00
String s = " Hello " ;
2020-07-27 12:43:20 +02:00
CHECK ( u32scmp ( s . get_data ( ) , U " Hello " ) = = 0 ) ;
2020-07-20 18:35:34 +02:00
}
2020-07-27 12:43:20 +02:00
TEST_CASE ( " [String] Assign from Latin-1 char string (operator=) " ) {
2020-07-20 18:35:34 +02:00
String s = " Dolly " ;
const String & t = s ;
2020-07-27 12:43:20 +02:00
CHECK ( u32scmp ( t . get_data ( ) , U " Dolly " ) = = 0 ) ;
2020-07-20 18:35:34 +02:00
}
2020-07-27 12:43:20 +02:00
TEST_CASE ( " [String] Assign from Latin-1 char string (copycon) " ) {
2020-07-20 18:35:34 +02:00
String s ( " Sheep " ) ;
2020-07-27 12:43:20 +02:00
const String & t1 ( s ) ;
CHECK ( u32scmp ( t1 . get_data ( ) , U " Sheep " ) = = 0 ) ;
String t2 = String ( " Sheep " , 3 ) ;
CHECK ( u32scmp ( t2 . get_data ( ) , U " She " ) = = 0 ) ;
2020-07-20 18:35:34 +02:00
}
2020-07-27 12:43:20 +02:00
TEST_CASE ( " [String] Assign from wchar_t string (operator=) " ) {
String s = L " Give me " ;
CHECK ( u32scmp ( s . get_data ( ) , U " Give me " ) = = 0 ) ;
2020-07-20 18:35:34 +02:00
}
2020-07-27 12:43:20 +02:00
TEST_CASE ( " [String] Assign from wchar_t string (copycon) " ) {
2020-07-20 18:35:34 +02:00
String s ( L " Wool " ) ;
2020-07-27 12:43:20 +02:00
CHECK ( u32scmp ( s . get_data ( ) , U " Wool " ) = = 0 ) ;
}
TEST_CASE ( " [String] Assign from char32_t string (operator=) " ) {
String s = U " Give me " ;
CHECK ( u32scmp ( s . get_data ( ) , U " Give me " ) = = 0 ) ;
}
TEST_CASE ( " [String] Assign from char32_t string (copycon) " ) {
String s ( U " Wool " ) ;
CHECK ( u32scmp ( s . get_data ( ) , U " Wool " ) = = 0 ) ;
}
TEST_CASE ( " [String] UTF8 " ) {
/* how can i embed UTF in here? */
static const char32_t u32str [ ] = { 0x0045 , 0x0020 , 0x304A , 0x360F , 0x3088 , 0x3046 , 0x1F3A4 , 0 } ;
static const uint8_t u8str [ ] = { 0x45 , 0x20 , 0xE3 , 0x81 , 0x8A , 0xE3 , 0x98 , 0x8F , 0xE3 , 0x82 , 0x88 , 0xE3 , 0x81 , 0x86 , 0xF0 , 0x9F , 0x8E , 0xA4 , 0 } ;
String s = u32str ;
2022-07-05 14:18:29 +02:00
Error err = s . parse_utf8 ( s . utf8 ( ) . get_data ( ) ) ;
CHECK ( err = = OK ) ;
2020-07-27 12:43:20 +02:00
CHECK ( s = = u32str ) ;
err = s . parse_utf8 ( ( const char * ) u8str ) ;
2022-07-05 14:18:29 +02:00
CHECK ( err = = OK ) ;
2020-07-27 12:43:20 +02:00
CHECK ( s = = u32str ) ;
CharString cs = ( const char * ) u8str ;
CHECK ( String : : utf8 ( cs ) = = s ) ;
}
TEST_CASE ( " [String] UTF16 " ) {
/* how can i embed UTF in here? */
static const char32_t u32str [ ] = { 0x0045 , 0x0020 , 0x304A , 0x360F , 0x3088 , 0x3046 , 0x1F3A4 , 0 } ;
static const char16_t u16str [ ] = { 0x0045 , 0x0020 , 0x304A , 0x360F , 0x3088 , 0x3046 , 0xD83C , 0xDFA4 , 0 } ;
String s = u32str ;
2022-07-05 14:18:29 +02:00
Error err = s . parse_utf16 ( s . utf16 ( ) . get_data ( ) ) ;
CHECK ( err = = OK ) ;
2020-07-27 12:43:20 +02:00
CHECK ( s = = u32str ) ;
err = s . parse_utf16 ( u16str ) ;
2022-07-05 14:18:29 +02:00
CHECK ( err = = OK ) ;
2020-07-27 12:43:20 +02:00
CHECK ( s = = u32str ) ;
Char16String cs = u16str ;
CHECK ( String : : utf16 ( cs ) = = s ) ;
}
TEST_CASE ( " [String] UTF8 with BOM " ) {
/* how can i embed UTF in here? */
static const char32_t u32str [ ] = { 0x0045 , 0x0020 , 0x304A , 0x360F , 0x3088 , 0x3046 , 0x1F3A4 , 0 } ;
static const uint8_t u8str [ ] = { 0xEF , 0xBB , 0xBF , 0x45 , 0x20 , 0xE3 , 0x81 , 0x8A , 0xE3 , 0x98 , 0x8F , 0xE3 , 0x82 , 0x88 , 0xE3 , 0x81 , 0x86 , 0xF0 , 0x9F , 0x8E , 0xA4 , 0 } ;
String s ;
2022-07-05 14:18:29 +02:00
Error err = s . parse_utf8 ( ( const char * ) u8str ) ;
CHECK ( err = = OK ) ;
2020-07-27 12:43:20 +02:00
CHECK ( s = = u32str ) ;
CharString cs = ( const char * ) u8str ;
CHECK ( String : : utf8 ( cs ) = = s ) ;
}
TEST_CASE ( " [String] UTF16 with BOM " ) {
/* how can i embed UTF in here? */
static const char32_t u32str [ ] = { 0x0020 , 0x0045 , 0x304A , 0x360F , 0x3088 , 0x3046 , 0x1F3A4 , 0 } ;
static const char16_t u16str [ ] = { 0xFEFF , 0x0020 , 0x0045 , 0x304A , 0x360F , 0x3088 , 0x3046 , 0xD83C , 0xDFA4 , 0 } ;
static const char16_t u16str_swap [ ] = { 0xFFFE , 0x2000 , 0x4500 , 0x4A30 , 0x0F36 , 0x8830 , 0x4630 , 0x3CD8 , 0xA4DF , 0 } ;
String s ;
2022-07-05 14:18:29 +02:00
Error err = s . parse_utf16 ( u16str ) ;
CHECK ( err = = OK ) ;
2020-07-27 12:43:20 +02:00
CHECK ( s = = u32str ) ;
err = s . parse_utf16 ( u16str_swap ) ;
2022-07-05 14:18:29 +02:00
CHECK ( err = = OK ) ;
2020-07-27 12:43:20 +02:00
CHECK ( s = = u32str ) ;
Char16String cs = u16str ;
CHECK ( String : : utf16 ( cs ) = = s ) ;
cs = u16str_swap ;
CHECK ( String : : utf16 ( cs ) = = s ) ;
}
2022-07-31 18:46:53 +02:00
TEST_CASE ( " [String] UTF8 with CR " ) {
const String base = U " Hello darkness \r \n My old friend \n I've come to talk \r With you again " ;
String keep_cr ;
Error err = keep_cr . parse_utf8 ( base . utf8 ( ) . get_data ( ) ) ;
CHECK ( err = = OK ) ;
CHECK ( keep_cr = = base ) ;
String no_cr ;
err = no_cr . parse_utf8 ( base . utf8 ( ) . get_data ( ) , - 1 , true ) ; // Skip CR.
CHECK ( err = = OK ) ;
CHECK ( no_cr = = base . replace ( " \r " , " " ) ) ;
}
2022-07-05 14:18:29 +02:00
TEST_CASE ( " [String] Invalid UTF8 (non-standard) " ) {
2020-07-27 12:43:20 +02:00
ERR_PRINT_OFF
2022-07-05 14:18:29 +02:00
static const uint8_t u8str [ ] = { 0x45 , 0xE3 , 0x81 , 0x8A , 0xE3 , 0x82 , 0x88 , 0xE3 , 0x81 , 0x86 , 0xF0 , 0x9F , 0x8E , 0xA4 , 0xF0 , 0x82 , 0x82 , 0xAC , 0xED , 0xA0 , 0x81 , 0 } ;
// + +2 +2 +2 +3 overlong +3 unpaired +2
2023-03-11 10:31:41 +01:00
static const char32_t u32str [ ] = { 0x45 , 0x304A , 0x3088 , 0x3046 , 0x1F3A4 , 0x20AC , 0xFFFD , 0 } ;
2020-07-27 12:43:20 +02:00
String s ;
2022-07-05 14:18:29 +02:00
Error err = s . parse_utf8 ( ( const char * ) u8str ) ;
2023-03-11 10:31:41 +01:00
CHECK ( err = = ERR_INVALID_DATA ) ;
2022-07-05 14:18:29 +02:00
CHECK ( s = = u32str ) ;
2020-07-27 12:43:20 +02:00
CharString cs = ( const char * ) u8str ;
2022-07-05 14:18:29 +02:00
CHECK ( String : : utf8 ( cs ) = = s ) ;
2020-07-27 12:43:20 +02:00
ERR_PRINT_ON
}
2022-07-05 14:18:29 +02:00
TEST_CASE ( " [String] Invalid UTF8 (unrecoverable) " ) {
ERR_PRINT_OFF
static const uint8_t u8str [ ] = { 0x45 , 0xE3 , 0x81 , 0x8A , 0x8F , 0xE3 , 0xE3 , 0x98 , 0x8F , 0xE3 , 0x82 , 0x88 , 0xE3 , 0x81 , 0x86 , 0xC0 , 0x80 , 0xF0 , 0x9F , 0x8E , 0xA4 , 0xF0 , 0x82 , 0x82 , 0xAC , 0xED , 0xA0 , 0x81 , 0 } ;
// + +2 inv +2 inv inv inv +2 +2 ovl NUL +1 +3 overlong +3 unpaired +2
2023-03-11 10:31:41 +01:00
static const char32_t u32str [ ] = { 0x45 , 0x304A , 0xFFFD , 0xFFFD , 0xFFFD , 0xFFFD , 0x3088 , 0x3046 , 0xFFFD , 0x1F3A4 , 0x20AC , 0xFFFD , 0 } ;
2022-07-05 14:18:29 +02:00
String s ;
Error err = s . parse_utf8 ( ( const char * ) u8str ) ;
CHECK ( err = = ERR_INVALID_DATA ) ;
CHECK ( s = = u32str ) ;
CharString cs = ( const char * ) u8str ;
CHECK ( String : : utf8 ( cs ) = = s ) ;
ERR_PRINT_ON
}
TEST_CASE ( " [String] Invalid UTF16 (non-standard) " ) {
2020-07-27 12:43:20 +02:00
ERR_PRINT_OFF
static const char16_t u16str [ ] = { 0x0045 , 0x304A , 0x3088 , 0x3046 , 0xDFA4 , 0 } ;
2022-07-05 14:18:29 +02:00
// + + + + unpaired
static const char32_t u32str [ ] = { 0x0045 , 0x304A , 0x3088 , 0x3046 , 0xDFA4 , 0 } ;
2020-07-27 12:43:20 +02:00
String s ;
2022-07-05 14:18:29 +02:00
Error err = s . parse_utf16 ( u16str ) ;
CHECK ( err = = ERR_PARSE_ERROR ) ;
CHECK ( s = = u32str ) ;
2020-07-27 12:43:20 +02:00
Char16String cs = u16str ;
2022-07-05 14:18:29 +02:00
CHECK ( String : : utf16 ( cs ) = = s ) ;
2020-07-27 12:43:20 +02:00
ERR_PRINT_ON
}
TEST_CASE ( " [String] ASCII " ) {
String s = U " Primero Leche " ;
String t = s . ascii ( false ) . get_data ( ) ;
CHECK ( s = = t ) ;
t = s . ascii ( true ) . get_data ( ) ;
CHECK ( s = = t ) ;
2020-07-20 18:35:34 +02:00
}
TEST_CASE ( " [String] Comparisons (equal) " ) {
String s = " Test Compare " ;
CHECK ( s = = " Test Compare " ) ;
2020-07-27 12:43:20 +02:00
CHECK ( s = = U " Test Compare " ) ;
2020-07-20 18:35:34 +02:00
CHECK ( s = = L " Test Compare " ) ;
CHECK ( s = = String ( " Test Compare " ) ) ;
2022-11-18 14:17:37 +01:00
CharString empty = " " ;
CharString cs = " Test Compare " ;
CHECK ( ! ( empty = = cs ) ) ;
CHECK ( ! ( cs = = empty ) ) ;
CHECK ( cs = = CharString ( " Test Compare " ) ) ;
2020-07-20 18:35:34 +02:00
}
TEST_CASE ( " [String] Comparisons (not equal) " ) {
String s = " Test Compare " ;
CHECK ( s ! = " Peanut " ) ;
2020-07-27 12:43:20 +02:00
CHECK ( s ! = U " Coconut " ) ;
2020-07-20 18:35:34 +02:00
CHECK ( s ! = L " Coconut " ) ;
CHECK ( s ! = String ( " Butter " ) ) ;
}
TEST_CASE ( " [String] Comparisons (operator <) " ) {
String s = " Bees " ;
CHECK ( s < " Elephant " ) ;
2020-07-27 12:43:20 +02:00
CHECK ( ! ( s < U " Amber " ) ) ;
2020-07-20 18:35:34 +02:00
CHECK ( ! ( s < L " Amber " ) ) ;
CHECK ( ! ( s < String ( " Beatrix " ) ) ) ;
}
TEST_CASE ( " [String] Concatenation " ) {
String s ;
s + = " Have " ;
s + = ' ' ;
s + = ' a ' ;
s + = String ( " " ) ;
2020-07-27 12:43:20 +02:00
s = s + U " Nice " ;
2020-07-20 18:35:34 +02:00
s = s + " " ;
s = s + String ( " Day " ) ;
CHECK ( s = = " Have a Nice Day " ) ;
}
TEST_CASE ( " [String] Testing size and length of string " ) {
// todo: expand this test to do more tests on size() as it is complicated under the hood.
CHECK ( String ( " Mellon " ) . size ( ) = = 7 ) ;
CHECK ( String ( " Mellon1 " ) . size ( ) = = 8 ) ;
// length works fine and is easier to test
CHECK ( String ( " Mellon " ) . length ( ) = = 6 ) ;
CHECK ( String ( " Mellon1 " ) . length ( ) = = 7 ) ;
CHECK ( String ( " Mellon2 " ) . length ( ) = = 7 ) ;
CHECK ( String ( " Mellon3 " ) . length ( ) = = 7 ) ;
}
TEST_CASE ( " [String] Testing for empty string " ) {
2020-12-15 13:04:21 +01:00
CHECK ( ! String ( " Mellon " ) . is_empty ( ) ) ;
2020-07-20 18:35:34 +02:00
// do this more than once, to check for string corruption
2020-12-15 13:04:21 +01:00
CHECK ( String ( " " ) . is_empty ( ) ) ;
CHECK ( String ( " " ) . is_empty ( ) ) ;
CHECK ( String ( " " ) . is_empty ( ) ) ;
2020-07-20 18:35:34 +02:00
}
2022-02-03 17:03:38 +01:00
TEST_CASE ( " [String] Contains " ) {
String s = " C: \\ Godot \\ project \\ string_test.tscn " ;
CHECK ( s . contains ( " : \\ " ) ) ;
CHECK ( s . contains ( " Godot " ) ) ;
CHECK ( s . contains ( String ( " project \\ string_test " ) ) ) ;
CHECK ( s . contains ( String ( " \\ string_test.tscn " ) ) ) ;
CHECK ( ! s . contains ( " :// " ) ) ;
CHECK ( ! s . contains ( " Godoh " ) ) ;
CHECK ( ! s . contains ( String ( " project \\ string test " ) ) ) ;
CHECK ( ! s . contains ( String ( " \\ char_test.tscn " ) ) ) ;
}
2020-07-27 12:43:20 +02:00
TEST_CASE ( " [String] Test chr " ) {
CHECK ( String : : chr ( ' H ' ) = = " H " ) ;
CHECK ( String : : chr ( 0x3012 ) [ 0 ] = = 0x3012 ) ;
ERR_PRINT_OFF
2023-03-11 10:31:41 +01:00
CHECK ( String : : chr ( 0xd812 ) [ 0 ] = = 0xfffd ) ; // Unpaired UTF-16 surrogate
CHECK ( String : : chr ( 0x20d812 ) [ 0 ] = = 0xfffd ) ; // Outside UTF-32 range
2020-07-27 12:43:20 +02:00
ERR_PRINT_ON
}
2020-07-20 18:35:34 +02:00
TEST_CASE ( " [String] Operator [] " ) {
String a = " Kugar Sane " ;
a [ 0 ] = ' S ' ;
a [ 6 ] = ' C ' ;
CHECK ( a = = " Sugar Cane " ) ;
CHECK ( a [ 1 ] = = ' u ' ) ;
2020-11-23 12:47:11 +01:00
CHECK ( a . unicode_at ( 1 ) = = ' u ' ) ;
2020-07-20 18:35:34 +02:00
}
TEST_CASE ( " [String] Case function test " ) {
String a = " MoMoNgA " ;
CHECK ( a . to_upper ( ) = = " MOMONGA " ) ;
2020-07-27 12:43:20 +02:00
CHECK ( a . to_lower ( ) = = " momonga " ) ;
}
TEST_CASE ( " [String] Case compare function test " ) {
String a = " MoMoNgA " ;
CHECK ( a . casecmp_to ( " momonga " ) ! = 0 ) ;
2020-07-20 18:35:34 +02:00
CHECK ( a . nocasecmp_to ( " momonga " ) = = 0 ) ;
}
2020-07-27 12:43:20 +02:00
TEST_CASE ( " [String] Natural compare function test " ) {
String a = " img2.png " ;
CHECK ( a . nocasecmp_to ( " img10.png " ) > 0 ) ;
CHECK ( a . naturalnocasecmp_to ( " img10.png " ) < 0 ) ;
2020-07-20 18:35:34 +02:00
}
2020-07-27 12:43:20 +02:00
TEST_CASE ( " [String] hex_encode_buffer " ) {
static const uint8_t u8str [ ] = { 0x45 , 0xE3 , 0x81 , 0x8A , 0x8F , 0xE3 } ;
String s = String : : hex_encode_buffer ( u8str , 6 ) ;
CHECK ( s = = U " 45e3818a8fe3 " ) ;
2020-07-20 18:35:34 +02:00
}
TEST_CASE ( " [String] Substr " ) {
String s = " Killer Baby " ;
CHECK ( s . substr ( 3 , 4 ) = = " ler " ) ;
2020-02-13 16:42:49 +01:00
CHECK ( s . substr ( 3 ) = = " ler Baby " ) ;
2020-07-20 18:35:34 +02:00
}
2020-07-27 12:43:20 +02:00
TEST_CASE ( " [String] Find " ) {
String s = " Pretty Woman Woman " ;
2020-07-20 18:35:34 +02:00
CHECK ( s . find ( " tty " ) = = 3 ) ;
2020-07-27 12:43:20 +02:00
CHECK ( s . find ( " Wo " , 9 ) = = 13 ) ;
2020-07-20 18:35:34 +02:00
CHECK ( s . find ( " Revenge of the Monster Truck " ) = = - 1 ) ;
2020-07-27 12:43:20 +02:00
CHECK ( s . rfind ( " man " ) = = 15 ) ;
2020-07-20 18:35:34 +02:00
}
2020-07-27 12:43:20 +02:00
TEST_CASE ( " [String] Find no case " ) {
String s = " Pretty Whale Whale " ;
2020-07-20 18:35:34 +02:00
CHECK ( s . findn ( " WHA " ) = = 7 ) ;
2020-07-27 12:43:20 +02:00
CHECK ( s . findn ( " WHA " , 9 ) = = 13 ) ;
2020-07-20 18:35:34 +02:00
CHECK ( s . findn ( " Revenge of the Monster SawFish " ) = = - 1 ) ;
2020-07-27 12:43:20 +02:00
CHECK ( s . rfindn ( " WHA " ) = = 13 ) ;
}
TEST_CASE ( " [String] Find MK " ) {
Vector < String > keys ;
keys . push_back ( " sty " ) ;
keys . push_back ( " tty " ) ;
keys . push_back ( " man " ) ;
String s = " Pretty Woman " ;
int key = 0 ;
CHECK ( s . findmk ( keys , 0 , & key ) = = 3 ) ;
CHECK ( key = = 1 ) ;
CHECK ( s . findmk ( keys , 5 , & key ) = = 9 ) ;
CHECK ( key = = 2 ) ;
2020-07-20 18:35:34 +02:00
}
TEST_CASE ( " [String] Find and replace " ) {
String s = " Happy Birthday, Anna! " ;
s = s . replace ( " Birthday " , " Halloween " ) ;
CHECK ( s = = " Happy Halloween, Anna! " ) ;
2020-07-27 12:43:20 +02:00
s = s . replace_first ( " H " , " W " ) ;
CHECK ( s = = " Wappy Halloween, Anna! " ) ;
2020-07-20 18:35:34 +02:00
}
TEST_CASE ( " [String] Insertion " ) {
String s = " Who is Frederic? " ;
s = s . insert ( s . find ( " ? " ) , " Chopin " ) ;
CHECK ( s = = " Who is Frederic Chopin? " ) ;
}
2023-03-30 15:45:45 +02:00
TEST_CASE ( " [String] Erasing " ) {
String s = " Josephine is such a cute girl! " ;
s = s . erase ( s . find ( " cute " ) , String ( " cute " ) . length ( ) ) ;
CHECK ( s = = " Josephine is such a girl! " ) ;
}
2020-07-20 18:35:34 +02:00
TEST_CASE ( " [String] Number to string " ) {
2021-08-17 11:43:11 +02:00
CHECK ( String : : num ( 0 ) = = " 0 " ) ;
CHECK ( String : : num ( 0.0 ) = = " 0 " ) ; // No trailing zeros.
CHECK ( String : : num ( - 0.0 ) = = " -0 " ) ; // Includes sign even for zero.
2020-07-20 18:35:34 +02:00
CHECK ( String : : num ( 3.141593 ) = = " 3.141593 " ) ;
2020-07-27 12:43:20 +02:00
CHECK ( String : : num ( 3.141593 , 3 ) = = " 3.142 " ) ;
CHECK ( String : : num_scientific ( 30000000 ) = = " 3e+07 " ) ;
CHECK ( String : : num_int64 ( 3141593 ) = = " 3141593 " ) ;
CHECK ( String : : num_int64 ( 0xA141593 , 16 ) = = " a141593 " ) ;
CHECK ( String : : num_int64 ( 0xA141593 , 16 , true ) = = " A141593 " ) ;
2021-08-17 11:43:11 +02:00
CHECK ( String : : num ( 42.100023 , 4 ) = = " 42.1 " ) ; // No trailing zeros.
2021-08-19 00:49:37 +02:00
// String::num_real tests.
2022-01-15 04:17:01 +01:00
CHECK ( String : : num_real ( 1.0 ) = = " 1.0 " ) ;
CHECK ( String : : num_real ( 1.0 , false ) = = " 1 " ) ;
CHECK ( String : : num_real ( 9.9 ) = = " 9.9 " ) ;
CHECK ( String : : num_real ( 9.99 ) = = " 9.99 " ) ;
CHECK ( String : : num_real ( 9.999 ) = = " 9.999 " ) ;
CHECK ( String : : num_real ( 9.9999 ) = = " 9.9999 " ) ;
2021-08-19 00:49:37 +02:00
CHECK ( String : : num_real ( 3.141593 ) = = " 3.141593 " ) ;
CHECK ( String : : num_real ( 3.141 ) = = " 3.141 " ) ; // No trailing zeros.
# ifdef REAL_T_IS_DOUBLE
2022-10-16 00:36:22 +02:00
CHECK_MESSAGE ( String : : num_real ( 123.456789 ) = = " 123.456789 " , " Prints the appropriate amount of digits for real_t = double. " ) ;
CHECK_MESSAGE ( String : : num_real ( - 123.456789 ) = = " -123.456789 " , " Prints the appropriate amount of digits for real_t = double. " ) ;
2021-08-19 00:49:37 +02:00
CHECK_MESSAGE ( String : : num_real ( Math_PI ) = = " 3.14159265358979 " , " Prints the appropriate amount of digits for real_t = double. " ) ;
2022-01-15 04:17:01 +01:00
CHECK_MESSAGE ( String : : num_real ( 3.1415f ) = = " 3.1414999961853 " , " Prints more digits of 32-bit float when real_t = double (ones that would be reliable for double) and no trailing zero. " ) ;
2021-08-19 00:49:37 +02:00
# else
2022-10-16 00:36:22 +02:00
CHECK_MESSAGE ( String : : num_real ( 123.456789 ) = = " 123.4568 " , " Prints the appropriate amount of digits for real_t = float. " ) ;
CHECK_MESSAGE ( String : : num_real ( - 123.456789 ) = = " -123.4568 " , " Prints the appropriate amount of digits for real_t = float. " ) ;
2021-08-19 00:49:37 +02:00
CHECK_MESSAGE ( String : : num_real ( Math_PI ) = = " 3.141593 " , " Prints the appropriate amount of digits for real_t = float. " ) ;
CHECK_MESSAGE ( String : : num_real ( 3.1415f ) = = " 3.1415 " , " Prints only reliable digits of 32-bit float when real_t = float. " ) ;
# endif // REAL_T_IS_DOUBLE
2021-08-17 11:43:11 +02:00
// Checks doubles with many decimal places.
CHECK ( String : : num ( 0.0000012345432123454321 , - 1 ) = = " 0.00000123454321 " ) ; // -1 uses 14 as sane default.
CHECK ( String : : num ( 0.0000012345432123454321 ) = = " 0.00000123454321 " ) ; // -1 is the default value.
CHECK ( String : : num ( - 0.0000012345432123454321 ) = = " -0.00000123454321 " ) ;
CHECK ( String : : num ( - 10000.0000012345432123454321 ) = = " -10000.0000012345 " ) ;
CHECK ( String : : num ( 0.0000000000012345432123454321 ) = = " 0.00000000000123 " ) ;
CHECK ( String : : num ( 0.0000000000012345432123454321 , 3 ) = = " 0 " ) ;
// Note: When relevant (remainder > 0.5), the last digit gets rounded up,
// which can also lead to not include a trailing zero, e.g. "...89" -> "...9".
CHECK ( String : : num ( 0.0000056789876567898765 ) = = " 0.00000567898766 " ) ; // Should round last digit.
CHECK ( String : : num ( 10000.000005678999999999 ) = = " 10000.000005679 " ) ; // We cut at ...789|99 which is rounded to ...79, so only 13 decimals.
CHECK ( String : : num ( 42.12999999 , 6 ) = = " 42.13 " ) ; // Also happens with lower decimals count.
// 32 is MAX_DECIMALS. We can't reliably store that many so we can't compare against a string,
// but we can check that the string length is 34 (32 + 2 for "0.").
CHECK ( String : : num ( 0.00000123456789987654321123456789987654321 , 32 ) . length ( ) = = 34 ) ;
CHECK ( String : : num ( 0.00000123456789987654321123456789987654321 , 42 ) . length ( ) = = 34 ) ; // Should enforce MAX_DECIMALS.
CHECK ( String : : num ( 10000.00000123456789987654321123456789987654321 , 42 ) . length ( ) = = 38 ) ; // 32 decimals + "10000.".
2014-02-10 02:10:30 +01:00
}
2020-07-20 18:35:34 +02:00
TEST_CASE ( " [String] String to integer " ) {
static const char * nums [ 4 ] = { " 1237461283 " , " - 22 " , " 0 " , " - 1123412 " } ;
static const int num [ 4 ] = { 1237461283 , - 22 , 0 , - 1123412 } ;
for ( int i = 0 ; i < 4 ; i + + ) {
CHECK ( String ( nums [ i ] ) . to_int ( ) = = num [ i ] ) ;
}
}
2020-07-27 12:43:20 +02:00
TEST_CASE ( " [String] Hex to integer " ) {
static const char * nums [ 4 ] = { " 0xFFAE " , " 22 " , " 0 " , " AADDAD " } ;
static const int64_t num [ 4 ] = { 0xFFAE , 0x22 , 0 , 0xAADDAD } ;
for ( int i = 0 ; i < 4 ; i + + ) {
2021-01-28 13:39:05 +01:00
CHECK ( String ( nums [ i ] ) . hex_to_int ( ) = = num [ i ] ) ;
2020-07-27 12:43:20 +02:00
}
}
2020-07-20 18:35:34 +02:00
TEST_CASE ( " [String] String to float " ) {
static const char * nums [ 4 ] = { " -12348298412.2 " , " 0.05 " , " 2.0002 " , " -0.0001 " } ;
static const double num [ 4 ] = { - 12348298412.2 , 0.05 , 2.0002 , - 0.0001 } ;
for ( int i = 0 ; i < 4 ; i + + ) {
2020-07-24 20:07:57 +02:00
CHECK ( ! ( ABS ( String ( nums [ i ] ) . to_float ( ) - num [ i ] ) > 0.00001 ) ) ;
2020-07-20 18:35:34 +02:00
}
}
TEST_CASE ( " [String] Slicing " ) {
String s = " Mars,Jupiter,Saturn,Uranus " ;
const char * slices [ 4 ] = { " Mars " , " Jupiter " , " Saturn " , " Uranus " } ;
for ( int i = 0 ; i < s . get_slice_count ( " , " ) ; i + + ) {
CHECK ( s . get_slice ( " , " , i ) = = slices [ i ] ) ;
}
}
2020-07-27 12:43:20 +02:00
TEST_CASE ( " [String] Splitting " ) {
String s = " Mars,Jupiter,Saturn,Uranus " ;
Vector < String > l ;
const char * slices_l [ 3 ] = { " Mars " , " Jupiter " , " Saturn,Uranus " } ;
const char * slices_r [ 3 ] = { " Mars,Jupiter " , " Saturn " , " Uranus " } ;
2022-08-12 16:21:15 +02:00
const char * slices_3 [ 4 ] = { " t " , " e " , " s " , " t " } ;
2020-07-27 12:43:20 +02:00
l = s . split ( " , " , true , 2 ) ;
CHECK ( l . size ( ) = = 3 ) ;
for ( int i = 0 ; i < l . size ( ) ; i + + ) {
CHECK ( l [ i ] = = slices_l [ i ] ) ;
}
l = s . rsplit ( " , " , true , 2 ) ;
CHECK ( l . size ( ) = = 3 ) ;
for ( int i = 0 ; i < l . size ( ) ; i + + ) {
CHECK ( l [ i ] = = slices_r [ i ] ) ;
}
2022-08-12 16:21:15 +02:00
s = " test " ;
l = s . split ( ) ;
CHECK ( l . size ( ) = = 4 ) ;
for ( int i = 0 ; i < l . size ( ) ; i + + ) {
CHECK ( l [ i ] = = slices_3 [ i ] ) ;
}
2023-02-01 23:29:17 +01:00
s = " " ;
l = s . split ( ) ;
CHECK ( l . size ( ) = = 1 ) ;
CHECK ( l [ 0 ] = = " " ) ;
l = s . split ( " " , false ) ;
CHECK ( l . size ( ) = = 0 ) ;
2020-07-27 12:43:20 +02:00
s = " Mars Jupiter Saturn Uranus " ;
const char * slices_s [ 4 ] = { " Mars " , " Jupiter " , " Saturn " , " Uranus " } ;
l = s . split_spaces ( ) ;
for ( int i = 0 ; i < l . size ( ) ; i + + ) {
CHECK ( l [ i ] = = slices_s [ i ] ) ;
}
s = " 1.2;2.3 4.5 " ;
const double slices_d [ 3 ] = { 1.2 , 2.3 , 4.5 } ;
2022-11-20 12:29:50 +01:00
Vector < double > d_arr ;
d_arr = s . split_floats ( " ; " ) ;
CHECK ( d_arr . size ( ) = = 2 ) ;
for ( int i = 0 ; i < d_arr . size ( ) ; i + + ) {
CHECK ( ABS ( d_arr [ i ] - slices_d [ i ] ) < = 0.00001 ) ;
2020-07-27 12:43:20 +02:00
}
Vector < String > keys ;
keys . push_back ( " ; " ) ;
keys . push_back ( " " ) ;
2022-11-20 12:29:50 +01:00
Vector < float > f_arr ;
f_arr = s . split_floats_mk ( keys ) ;
CHECK ( f_arr . size ( ) = = 3 ) ;
for ( int i = 0 ; i < f_arr . size ( ) ; i + + ) {
CHECK ( ABS ( f_arr [ i ] - slices_d [ i ] ) < = 0.00001 ) ;
2020-07-27 12:43:20 +02:00
}
s = " 1;2 4 " ;
const int slices_i [ 3 ] = { 1 , 2 , 4 } ;
Vector < int > ii ;
ii = s . split_ints ( " ; " ) ;
CHECK ( ii . size ( ) = = 2 ) ;
for ( int i = 0 ; i < ii . size ( ) ; i + + ) {
CHECK ( ii [ i ] = = slices_i [ i ] ) ;
}
ii = s . split_ints_mk ( keys ) ;
CHECK ( ii . size ( ) = = 3 ) ;
for ( int i = 0 ; i < ii . size ( ) ; i + + ) {
CHECK ( ii [ i ] = = slices_i [ i ] ) ;
}
}
2020-07-20 18:35:34 +02:00
struct test_27_data {
char const * data ;
2020-07-27 12:43:20 +02:00
char const * part ;
2020-07-20 18:35:34 +02:00
bool expected ;
} ;
TEST_CASE ( " [String] Begins with " ) {
test_27_data tc [ ] = {
{ " res://foobar " , " res:// " , true } ,
{ " res " , " res:// " , false } ,
{ " abc " , " abc " , true }
} ;
size_t count = sizeof ( tc ) / sizeof ( tc [ 0 ] ) ;
bool state = true ;
for ( size_t i = 0 ; state & & i < count ; + + i ) {
String s = tc [ i ] . data ;
2020-07-27 12:43:20 +02:00
state = s . begins_with ( tc [ i ] . part ) = = tc [ i ] . expected ;
2020-07-20 18:35:34 +02:00
if ( state ) {
2020-07-27 12:43:20 +02:00
String sb = tc [ i ] . part ;
2020-07-20 18:35:34 +02:00
state = s . begins_with ( sb ) = = tc [ i ] . expected ;
}
CHECK ( state ) ;
if ( ! state ) {
break ;
}
} ;
CHECK ( state ) ;
}
2020-07-27 12:43:20 +02:00
TEST_CASE ( " [String] Ends with " ) {
test_27_data tc [ ] = {
{ " res://foobar " , " foobar " , true } ,
{ " res " , " res:// " , false } ,
{ " abc " , " abc " , true }
} ;
size_t count = sizeof ( tc ) / sizeof ( tc [ 0 ] ) ;
bool state = true ;
for ( size_t i = 0 ; state & & i < count ; + + i ) {
String s = tc [ i ] . data ;
state = s . ends_with ( tc [ i ] . part ) = = tc [ i ] . expected ;
if ( state ) {
String sb = tc [ i ] . part ;
state = s . ends_with ( sb ) = = tc [ i ] . expected ;
}
CHECK ( state ) ;
if ( ! state ) {
break ;
}
} ;
CHECK ( state ) ;
}
TEST_CASE ( " [String] format " ) {
const String value_format = " red= \" $red \" green= \" $green \" blue= \" $blue \" alpha= \" $alpha \" " ;
Dictionary value_dictionary ;
value_dictionary [ " red " ] = 10 ;
value_dictionary [ " green " ] = 20 ;
value_dictionary [ " blue " ] = " bla " ;
value_dictionary [ " alpha " ] = 0.4 ;
String value = value_format . format ( value_dictionary , " $_ " ) ;
CHECK ( value = = " red= \" 10 \" green= \" 20 \" blue= \" bla \" alpha= \" 0.4 \" " ) ;
}
2020-07-20 18:35:34 +02:00
TEST_CASE ( " [String] sprintf " ) {
String format , output ;
Array args ;
bool error ;
// %%
format = " fish %% frog " ;
args . clear ( ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error = = false ) ;
CHECK ( output = = String ( " fish % frog " ) ) ;
2022-08-24 16:23:53 +02:00
///// Ints
2020-07-20 18:35:34 +02:00
// Int
format = " fish %d frog " ;
args . clear ( ) ;
args . push_back ( 5 ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error = = false ) ;
CHECK ( output = = String ( " fish 5 frog " ) ) ;
// Int left padded with zeroes.
format = " fish %05d frog " ;
args . clear ( ) ;
args . push_back ( 5 ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error = = false ) ;
CHECK ( output = = String ( " fish 00005 frog " ) ) ;
// Int left padded with spaces.
format = " fish %5d frog " ;
args . clear ( ) ;
args . push_back ( 5 ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error = = false ) ;
CHECK ( output = = String ( " fish 5 frog " ) ) ;
// Int right padded with spaces.
format = " fish %-5d frog " ;
args . clear ( ) ;
args . push_back ( 5 ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error = = false ) ;
CHECK ( output = = String ( " fish 5 frog " ) ) ;
// Int with sign (positive).
format = " fish %+d frog " ;
args . clear ( ) ;
args . push_back ( 5 ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error = = false ) ;
CHECK ( output = = String ( " fish +5 frog " ) ) ;
// Negative int.
format = " fish %d frog " ;
args . clear ( ) ;
args . push_back ( - 5 ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error = = false ) ;
CHECK ( output = = String ( " fish -5 frog " ) ) ;
2022-05-01 05:04:52 +02:00
// Negative int left padded with spaces.
format = " fish %5d frog " ;
args . clear ( ) ;
args . push_back ( - 5 ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error = = false ) ;
CHECK ( output = = String ( " fish -5 frog " ) ) ;
// Negative int left padded with zeros.
format = " fish %05d frog " ;
args . clear ( ) ;
args . push_back ( - 5 ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error = = false ) ;
CHECK ( output = = String ( " fish -0005 frog " ) ) ;
// Negative int right padded with spaces.
format = " fish %-5d frog " ;
args . clear ( ) ;
args . push_back ( - 5 ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error = = false ) ;
CHECK ( output = = String ( " fish -5 frog " ) ) ;
// Negative int right padded with zeros. (0 ignored)
format = " fish %-05d frog " ;
args . clear ( ) ;
args . push_back ( - 5 ) ;
2022-08-04 15:55:45 +02:00
ERR_PRINT_OFF ; // Silence warning about 0 ignored.
2022-05-01 05:04:52 +02:00
output = format . sprintf ( args , & error ) ;
2022-08-04 15:55:45 +02:00
ERR_PRINT_ON ;
2022-05-01 05:04:52 +02:00
REQUIRE ( error = = false ) ;
CHECK ( output = = String ( " fish -5 frog " ) ) ;
2020-07-20 18:35:34 +02:00
// Hex (lower)
format = " fish %x frog " ;
args . clear ( ) ;
args . push_back ( 45 ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error = = false ) ;
CHECK ( output = = String ( " fish 2d frog " ) ) ;
// Hex (upper)
format = " fish %X frog " ;
args . clear ( ) ;
args . push_back ( 45 ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error = = false ) ;
CHECK ( output = = String ( " fish 2D frog " ) ) ;
// Octal
format = " fish %o frog " ;
args . clear ( ) ;
args . push_back ( 99 ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error = = false ) ;
CHECK ( output = = String ( " fish 143 frog " ) ) ;
2022-08-24 16:23:53 +02:00
///// Reals
2020-07-20 18:35:34 +02:00
// Real
format = " fish %f frog " ;
args . clear ( ) ;
args . push_back ( 99.99 ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error = = false ) ;
CHECK ( output = = String ( " fish 99.990000 frog " ) ) ;
2022-08-24 16:23:53 +02:00
// Real left-padded.
2020-07-20 18:35:34 +02:00
format = " fish %11f frog " ;
args . clear ( ) ;
args . push_back ( 99.99 ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error = = false ) ;
CHECK ( output = = String ( " fish 99.990000 frog " ) ) ;
2022-08-24 15:41:31 +02:00
// Real (infinity) left-padded
format = " fish %11f frog " ;
args . clear ( ) ;
args . push_back ( INFINITY ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error = = false ) ;
CHECK ( output = = String ( " fish inf frog " ) ) ;
2022-08-24 16:23:53 +02:00
// Real right-padded.
2020-07-20 18:35:34 +02:00
format = " fish %-11f frog " ;
args . clear ( ) ;
args . push_back ( 99.99 ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error = = false ) ;
CHECK ( output = = String ( " fish 99.990000 frog " ) ) ;
// Real given int.
format = " fish %f frog " ;
args . clear ( ) ;
args . push_back ( 99 ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error = = false ) ;
CHECK ( output = = String ( " fish 99.000000 frog " ) ) ;
// Real with sign (positive).
format = " fish %+f frog " ;
args . clear ( ) ;
args . push_back ( 99.99 ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error = = false ) ;
CHECK ( output = = String ( " fish +99.990000 frog " ) ) ;
2022-08-24 16:23:53 +02:00
// Real with 1 decimal.
2020-07-20 18:35:34 +02:00
format = " fish %.1f frog " ;
args . clear ( ) ;
args . push_back ( 99.99 ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error = = false ) ;
CHECK ( output = = String ( " fish 100.0 frog " ) ) ;
// Real with 12 decimals.
format = " fish %.12f frog " ;
args . clear ( ) ;
args . push_back ( 99.99 ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error = = false ) ;
CHECK ( output = = String ( " fish 99.990000000000 frog " ) ) ;
// Real with no decimals.
format = " fish %.f frog " ;
args . clear ( ) ;
args . push_back ( 99.99 ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error = = false ) ;
CHECK ( output = = String ( " fish 100 frog " ) ) ;
2022-05-01 05:04:52 +02:00
// Negative real right padded with zeros. (0 ignored)
format = " fish %-011f frog " ;
args . clear ( ) ;
args . push_back ( - 99.99 ) ;
2022-08-04 15:55:45 +02:00
ERR_PRINT_OFF ; // Silence warning about 0 ignored.
2022-05-01 05:04:52 +02:00
output = format . sprintf ( args , & error ) ;
2022-08-04 15:55:45 +02:00
ERR_PRINT_ON ;
2022-05-01 05:04:52 +02:00
REQUIRE ( error = = false ) ;
CHECK ( output = = String ( " fish -99.990000 frog " ) ) ;
2022-08-24 16:23:53 +02:00
///// Vectors
2022-07-31 17:50:02 +02:00
// Vector2
format = " fish %v frog " ;
args . clear ( ) ;
args . push_back ( Variant ( Vector2 ( 19.99 , 1.00 ) ) ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error = = false ) ;
CHECK ( output = = String ( " fish (19.990000, 1.000000) frog " ) ) ;
// Vector3
format = " fish %v frog " ;
args . clear ( ) ;
args . push_back ( Variant ( Vector3 ( 19.99 , 1.00 , - 2.05 ) ) ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error = = false ) ;
CHECK ( output = = String ( " fish (19.990000, 1.000000, -2.050000) frog " ) ) ;
// Vector4
format = " fish %v frog " ;
args . clear ( ) ;
args . push_back ( Variant ( Vector4 ( 19.99 , 1.00 , - 2.05 , 5.5 ) ) ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error = = false ) ;
CHECK ( output = = String ( " fish (19.990000, 1.000000, -2.050000, 5.500000) frog " ) ) ;
2022-08-24 16:23:53 +02:00
// Vector with negative values.
2022-07-31 17:50:02 +02:00
format = " fish %v frog " ;
args . clear ( ) ;
args . push_back ( Variant ( Vector2 ( - 19.99 , - 1.00 ) ) ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error = = false ) ;
CHECK ( output = = String ( " fish (-19.990000, -1.000000) frog " ) ) ;
2022-08-24 16:23:53 +02:00
// Vector left-padded.
2022-07-31 17:50:02 +02:00
format = " fish %11v frog " ;
args . clear ( ) ;
args . push_back ( Variant ( Vector3 ( 19.99 , 1.00 , - 2.05 ) ) ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error = = false ) ;
CHECK ( output = = String ( " fish ( 19.990000, 1.000000, -2.050000) frog " ) ) ;
2022-08-24 15:41:31 +02:00
// Vector left-padded with inf/nan
format = " fish %11v frog " ;
args . clear ( ) ;
args . push_back ( Variant ( Vector2 ( INFINITY , NAN ) ) ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error = = false ) ;
CHECK ( output = = String ( " fish ( inf, nan) frog " ) ) ;
2022-07-31 17:50:02 +02:00
2022-08-24 16:23:53 +02:00
// Vector right-padded.
2022-07-31 17:50:02 +02:00
format = " fish %-11v frog " ;
args . clear ( ) ;
args . push_back ( Variant ( Vector3 ( 19.99 , 1.00 , - 2.05 ) ) ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error = = false ) ;
CHECK ( output = = String ( " fish (19.990000 , 1.000000 , -2.050000 ) frog " ) ) ;
2022-08-24 16:23:53 +02:00
// Vector left-padded with zeros.
2022-07-31 17:50:02 +02:00
format = " fish %011v frog " ;
args . clear ( ) ;
args . push_back ( Variant ( Vector3 ( 19.99 , 1.00 , - 2.05 ) ) ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error = = false ) ;
CHECK ( output = = String ( " fish (0019.990000, 0001.000000, -002.050000) frog " ) ) ;
// Vector given Vector3i.
format = " fish %v frog " ;
args . clear ( ) ;
args . push_back ( Variant ( Vector3i ( 19 , 1 , - 2 ) ) ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error = = false ) ;
CHECK ( output = = String ( " fish (19.000000, 1.000000, -2.000000) frog " ) ) ;
2022-08-24 16:23:53 +02:00
// Vector with 1 decimal.
2022-07-31 17:50:02 +02:00
format = " fish %.1v frog " ;
args . clear ( ) ;
args . push_back ( Variant ( Vector3 ( 19.99 , 1.00 , - 2.05 ) ) ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error = = false ) ;
CHECK ( output = = String ( " fish (20.0, 1.0, -2.0) frog " ) ) ;
// Vector with 12 decimals.
format = " fish %.12v frog " ;
args . clear ( ) ;
args . push_back ( Variant ( Vector3 ( 19.00 , 1.00 , - 2.00 ) ) ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error = = false ) ;
CHECK ( output = = String ( " fish (19.000000000000, 1.000000000000, -2.000000000000) frog " ) ) ;
// Vector with no decimals.
format = " fish %.v frog " ;
args . clear ( ) ;
args . push_back ( Variant ( Vector3 ( 19.99 , 1.00 , - 2.05 ) ) ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error = = false ) ;
CHECK ( output = = String ( " fish (20, 1, -2) frog " ) ) ;
2022-08-24 16:23:53 +02:00
///// Strings
2020-07-20 18:35:34 +02:00
// String
format = " fish %s frog " ;
args . clear ( ) ;
args . push_back ( " cheese " ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error = = false ) ;
CHECK ( output = = String ( " fish cheese frog " ) ) ;
2022-08-24 16:23:53 +02:00
// String left-padded.
2020-07-20 18:35:34 +02:00
format = " fish %10s frog " ;
args . clear ( ) ;
args . push_back ( " cheese " ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error = = false ) ;
CHECK ( output = = String ( " fish cheese frog " ) ) ;
2022-08-24 16:23:53 +02:00
// String right-padded.
2020-07-20 18:35:34 +02:00
format = " fish %-10s frog " ;
args . clear ( ) ;
args . push_back ( " cheese " ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error = = false ) ;
CHECK ( output = = String ( " fish cheese frog " ) ) ;
///// Characters
// Character as string.
format = " fish %c frog " ;
args . clear ( ) ;
args . push_back ( " A " ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error = = false ) ;
CHECK ( output = = String ( " fish A frog " ) ) ;
// Character as int.
format = " fish %c frog " ;
args . clear ( ) ;
args . push_back ( 65 ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error = = false ) ;
CHECK ( output = = String ( " fish A frog " ) ) ;
///// Dynamic width
2022-08-24 16:23:53 +02:00
// String dynamic width.
2020-07-20 18:35:34 +02:00
format = " fish %*s frog " ;
args . clear ( ) ;
args . push_back ( 10 ) ;
args . push_back ( " cheese " ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error = = false ) ;
REQUIRE ( output = = String ( " fish cheese frog " ) ) ;
2022-08-24 16:23:53 +02:00
// Int dynamic width.
2020-07-20 18:35:34 +02:00
format = " fish %*d frog " ;
args . clear ( ) ;
args . push_back ( 10 ) ;
args . push_back ( 99 ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error = = false ) ;
REQUIRE ( output = = String ( " fish 99 frog " ) ) ;
2022-08-24 16:23:53 +02:00
// Float dynamic width.
2020-07-20 18:35:34 +02:00
format = " fish %*.*f frog " ;
args . clear ( ) ;
args . push_back ( 10 ) ;
args . push_back ( 3 ) ;
args . push_back ( 99.99 ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error = = false ) ;
CHECK ( output = = String ( " fish 99.990 frog " ) ) ;
///// Errors
// More formats than arguments.
format = " fish %s %s frog " ;
args . clear ( ) ;
args . push_back ( " cheese " ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error ) ;
CHECK ( output = = " not enough arguments for format string " ) ;
// More arguments than formats.
format = " fish %s frog " ;
args . clear ( ) ;
args . push_back ( " hello " ) ;
args . push_back ( " cheese " ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error ) ;
CHECK ( output = = " not all arguments converted during string formatting " ) ;
// Incomplete format.
format = " fish %10 " ;
args . clear ( ) ;
args . push_back ( " cheese " ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error ) ;
CHECK ( output = = " incomplete format " ) ;
2022-08-24 16:23:53 +02:00
// Bad character in format string.
2020-07-20 18:35:34 +02:00
format = " fish %&f frog " ;
args . clear ( ) ;
args . push_back ( " cheese " ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error ) ;
CHECK ( output = = " unsupported format character " ) ;
// Too many decimals.
format = " fish %2.2.2f frog " ;
args . clear ( ) ;
args . push_back ( 99.99 ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error ) ;
CHECK ( output = = " too many decimal points in format " ) ;
2022-08-24 16:23:53 +02:00
// * not a number or vector.
2020-07-20 18:35:34 +02:00
format = " fish %*f frog " ;
args . clear ( ) ;
args . push_back ( " cheese " ) ;
args . push_back ( 99.99 ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error ) ;
2022-07-31 17:50:02 +02:00
CHECK ( output = = " * wants number or vector " ) ;
2020-07-20 18:35:34 +02:00
// Character too long.
format = " fish %c frog " ;
args . clear ( ) ;
args . push_back ( " sc " ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error ) ;
CHECK ( output = = " %c requires number or single-character string " ) ;
// Character bad type.
format = " fish %c frog " ;
args . clear ( ) ;
args . push_back ( Array ( ) ) ;
output = format . sprintf ( args , & error ) ;
REQUIRE ( error ) ;
CHECK ( output = = " %c requires number or single-character string " ) ;
}
2020-07-27 12:43:20 +02:00
TEST_CASE ( " [String] is_numeric " ) {
CHECK ( String ( " 12 " ) . is_numeric ( ) ) ;
CHECK ( String ( " 1.2 " ) . is_numeric ( ) ) ;
CHECK ( ! String ( " AF " ) . is_numeric ( ) ) ;
CHECK ( String ( " -12 " ) . is_numeric ( ) ) ;
CHECK ( String ( " -1.2 " ) . is_numeric ( ) ) ;
}
TEST_CASE ( " [String] pad " ) {
String s = String ( " test " ) ;
CHECK ( s . lpad ( 10 , " x " ) = = U " xxxxxxtest " ) ;
CHECK ( s . rpad ( 10 , " x " ) = = U " testxxxxxx " ) ;
s = String ( " 10.10 " ) ;
CHECK ( s . pad_decimals ( 4 ) = = U " 10.1000 " ) ;
CHECK ( s . pad_zeros ( 4 ) = = U " 0010.10 " ) ;
}
TEST_CASE ( " [String] is_subsequence_of " ) {
String a = " is subsequence of " ;
CHECK ( String ( " sub " ) . is_subsequence_of ( a ) ) ;
CHECK ( ! String ( " Sub " ) . is_subsequence_of ( a ) ) ;
2022-01-27 00:03:56 +01:00
CHECK ( String ( " Sub " ) . is_subsequence_ofn ( a ) ) ;
2020-07-27 12:43:20 +02:00
}
TEST_CASE ( " [String] match " ) {
CHECK ( String ( " img1.png " ) . match ( " *.png " ) ) ;
CHECK ( ! String ( " img1.jpeg " ) . match ( " *.png " ) ) ;
CHECK ( ! String ( " img1.Png " ) . match ( " *.png " ) ) ;
CHECK ( String ( " img1.Png " ) . matchn ( " *.png " ) ) ;
}
2020-07-20 18:35:34 +02:00
TEST_CASE ( " [String] IPVX address to string " ) {
2021-05-06 02:48:18 +02:00
IPAddress ip0 ( " 2001:0db8:85a3:0000:0000:8a2e:0370:7334 " ) ;
IPAddress ip ( 0x0123 , 0x4567 , 0x89ab , 0xcdef , true ) ;
IPAddress ip2 ( " fe80::52e5:49ff:fe93:1baf " ) ;
IPAddress ip3 ( " ::ffff:192.168.0.1 " ) ;
2020-07-20 18:35:34 +02:00
String ip4 = " 192.168.0.1 " ;
CHECK ( ip4 . is_valid_ip_address ( ) ) ;
ip4 = " 192.368.0.1 " ;
CHECK ( ! ip4 . is_valid_ip_address ( ) ) ;
String ip6 = " 2001:0db8:85a3:0000:0000:8a2e:0370:7334 " ;
CHECK ( ip6 . is_valid_ip_address ( ) ) ;
ip6 = " 2001:0db8:85j3:0000:0000:8a2e:0370:7334 " ;
CHECK ( ! ip6 . is_valid_ip_address ( ) ) ;
ip6 = " 2001:0db8:85f345:0000:0000:8a2e:0370:7334 " ;
CHECK ( ! ip6 . is_valid_ip_address ( ) ) ;
ip6 = " 2001:0db8::0:8a2e:370:7334 " ;
CHECK ( ip6 . is_valid_ip_address ( ) ) ;
ip6 = " ::ffff:192.168.0.1 " ;
CHECK ( ip6 . is_valid_ip_address ( ) ) ;
}
TEST_CASE ( " [String] Capitalize against many strings " ) {
2022-08-30 11:36:24 +02:00
String input = " 2D " ;
String output = " 2d " ;
CHECK ( input . capitalize ( ) = = output ) ;
input = " 2d " ;
output = " 2d " ;
CHECK ( input . capitalize ( ) = = output ) ;
input = " 2db " ;
output = " 2 Db " ;
CHECK ( input . capitalize ( ) = = output ) ;
input = " HTML5 Html5 html5 html_5 " ;
output = " Html 5 Html 5 Html 5 Html 5 " ;
CHECK ( input . capitalize ( ) = = output ) ;
input = " Node2D Node2d NODE2D NODE_2D node_2d " ;
output = " Node 2d Node 2d Node 2d Node 2d Node 2d " ;
CHECK ( input . capitalize ( ) = = output ) ;
input = " Node2DPosition " ;
output = " Node 2d Position " ;
CHECK ( input . capitalize ( ) = = output ) ;
input = " Number2Digits " ;
output = " Number 2 Digits " ;
CHECK ( input . capitalize ( ) = = output ) ;
input = " bytes2var " ;
output = " Bytes 2 Var " ;
2020-07-20 18:35:34 +02:00
CHECK ( input . capitalize ( ) = = output ) ;
input = " linear2db " ;
output = " Linear 2 Db " ;
CHECK ( input . capitalize ( ) = = output ) ;
input = " vector3 " ;
output = " Vector 3 " ;
CHECK ( input . capitalize ( ) = = output ) ;
input = " sha256 " ;
output = " Sha 256 " ;
CHECK ( input . capitalize ( ) = = output ) ;
input = " PascalCase " ;
output = " Pascal Case " ;
CHECK ( input . capitalize ( ) = = output ) ;
input = " PascalPascalCase " ;
output = " Pascal Pascal Case " ;
CHECK ( input . capitalize ( ) = = output ) ;
input = " snake_case " ;
output = " Snake Case " ;
CHECK ( input . capitalize ( ) = = output ) ;
input = " snake_snake_case " ;
output = " Snake Snake Case " ;
CHECK ( input . capitalize ( ) = = output ) ;
input = " sha256sum " ;
output = " Sha 256 Sum " ;
CHECK ( input . capitalize ( ) = = output ) ;
input = " cat2dog " ;
output = " Cat 2 Dog " ;
CHECK ( input . capitalize ( ) = = output ) ;
input = " function(name) " ;
output = " Function(name) " ;
CHECK ( input . capitalize ( ) = = output ) ;
input = " snake_case_function(snake_case_arg) " ;
output = " Snake Case Function(snake Case Arg) " ;
CHECK ( input . capitalize ( ) = = output ) ;
input = " snake_case_function( snake_case_arg ) " ;
output = " Snake Case Function( Snake Case Arg ) " ;
CHECK ( input . capitalize ( ) = = output ) ;
}
2022-08-30 11:36:24 +02:00
struct StringCasesTestCase {
const char * input ;
const char * camel_case ;
const char * pascal_case ;
const char * snake_case ;
} ;
TEST_CASE ( " [String] Checking case conversion methods " ) {
StringCasesTestCase test_cases [ ] = {
/* clang-format off */
{ " 2D " , " 2d " , " 2d " , " 2d " } ,
{ " 2d " , " 2d " , " 2d " , " 2d " } ,
{ " 2db " , " 2Db " , " 2Db " , " 2_db " } ,
{ " Vector3 " , " vector3 " , " Vector3 " , " vector_3 " } ,
{ " sha256 " , " sha256 " , " Sha256 " , " sha_256 " } ,
{ " Node2D " , " node2d " , " Node2d " , " node_2d " } ,
{ " RichTextLabel " , " richTextLabel " , " RichTextLabel " , " rich_text_label " } ,
{ " HTML5 " , " html5 " , " Html5 " , " html_5 " } ,
{ " Node2DPosition " , " node2dPosition " , " Node2dPosition " , " node_2d_position " } ,
{ " Number2Digits " , " number2Digits " , " Number2Digits " , " number_2_digits " } ,
{ " get_property_list " , " getPropertyList " , " GetPropertyList " , " get_property_list " } ,
{ " get_camera_2d " , " getCamera2d " , " GetCamera2d " , " get_camera_2d " } ,
{ " _physics_process " , " physicsProcess " , " PhysicsProcess " , " _physics_process " } ,
{ " bytes2var " , " bytes2Var " , " Bytes2Var " , " bytes_2_var " } ,
{ " linear2db " , " linear2Db " , " Linear2Db " , " linear_2_db " } ,
{ " sha256sum " , " sha256Sum " , " Sha256Sum " , " sha_256_sum " } ,
{ " camelCase " , " camelCase " , " CamelCase " , " camel_case " } ,
{ " PascalCase " , " pascalCase " , " PascalCase " , " pascal_case " } ,
{ " snake_case " , " snakeCase " , " SnakeCase " , " snake_case " } ,
{ " Test TEST test " , " testTestTest " , " TestTestTest " , " test_test_test " } ,
{ nullptr , nullptr , nullptr , nullptr } ,
/* clang-format on */
} ;
int idx = 0 ;
while ( test_cases [ idx ] . input ! = nullptr ) {
String input = test_cases [ idx ] . input ;
CHECK ( input . to_camel_case ( ) = = test_cases [ idx ] . camel_case ) ;
CHECK ( input . to_pascal_case ( ) = = test_cases [ idx ] . pascal_case ) ;
CHECK ( input . to_snake_case ( ) = = test_cases [ idx ] . snake_case ) ;
idx + + ;
}
}
2020-07-20 18:35:34 +02:00
TEST_CASE ( " [String] Checking string is empty when it should be " ) {
bool state = true ;
bool success ;
String a = " " ;
success = a [ 0 ] = = 0 ;
if ( ! success ) {
state = false ;
}
String b = " Godot " ;
success = b [ b . size ( ) ] = = 0 ;
if ( ! success ) {
state = false ;
}
const String c = " " ;
success = c [ 0 ] = = 0 ;
if ( ! success ) {
state = false ;
}
const String d = " Godot " ;
success = d [ d . size ( ) ] = = 0 ;
if ( ! success ) {
state = false ;
}
CHECK ( state ) ;
}
TEST_CASE ( " [String] lstrip and rstrip " ) {
# define STRIP_TEST(x) \
{ \
bool success = x ; \
state = state & & success ; \
}
bool state = true ;
// strip none
STRIP_TEST ( String ( " abc " ) . lstrip ( " " ) = = " abc " ) ;
STRIP_TEST ( String ( " abc " ) . rstrip ( " " ) = = " abc " ) ;
// strip one
STRIP_TEST ( String ( " abc " ) . lstrip ( " a " ) = = " bc " ) ;
STRIP_TEST ( String ( " abc " ) . rstrip ( " c " ) = = " ab " ) ;
// strip lots
STRIP_TEST ( String ( " bababbababccc " ) . lstrip ( " ab " ) = = " ccc " ) ;
STRIP_TEST ( String ( " aaabcbcbcbbcbbc " ) . rstrip ( " cb " ) = = " aaa " ) ;
// strip empty string
STRIP_TEST ( String ( " " ) . lstrip ( " " ) = = " " ) ;
STRIP_TEST ( String ( " " ) . rstrip ( " " ) = = " " ) ;
// strip to empty string
STRIP_TEST ( String ( " abcabcabc " ) . lstrip ( " bca " ) = = " " ) ;
STRIP_TEST ( String ( " abcabcabc " ) . rstrip ( " bca " ) = = " " ) ;
// don't strip wrong end
STRIP_TEST ( String ( " abc " ) . lstrip ( " c " ) = = " abc " ) ;
STRIP_TEST ( String ( " abca " ) . lstrip ( " a " ) = = " bca " ) ;
STRIP_TEST ( String ( " abc " ) . rstrip ( " a " ) = = " abc " ) ;
STRIP_TEST ( String ( " abca " ) . rstrip ( " a " ) = = " abc " ) ;
// in utf-8 "¿" (\u00bf) has the same first byte as "µ" (\u00b5)
// and the same second as "ÿ" (\u00ff)
STRIP_TEST ( String : : utf8 ( " ¿ " ) . lstrip ( String : : utf8 ( " µÿ " ) ) = = String : : utf8 ( " ¿ " ) ) ;
STRIP_TEST ( String : : utf8 ( " ¿ " ) . rstrip ( String : : utf8 ( " µÿ " ) ) = = String : : utf8 ( " ¿ " ) ) ;
STRIP_TEST ( String : : utf8 ( " µ¿ÿ " ) . lstrip ( String : : utf8 ( " µÿ " ) ) = = String : : utf8 ( " ¿ÿ " ) ) ;
STRIP_TEST ( String : : utf8 ( " µ¿ÿ " ) . rstrip ( String : : utf8 ( " µÿ " ) ) = = String : : utf8 ( " µ¿ " ) ) ;
// the above tests repeated with additional superfluous strip chars
// strip none
STRIP_TEST ( String ( " abc " ) . lstrip ( " qwjkl " ) = = " abc " ) ;
STRIP_TEST ( String ( " abc " ) . rstrip ( " qwjkl " ) = = " abc " ) ;
// strip one
STRIP_TEST ( String ( " abc " ) . lstrip ( " qwajkl " ) = = " bc " ) ;
STRIP_TEST ( String ( " abc " ) . rstrip ( " qwcjkl " ) = = " ab " ) ;
// strip lots
STRIP_TEST ( String ( " bababbababccc " ) . lstrip ( " qwabjkl " ) = = " ccc " ) ;
STRIP_TEST ( String ( " aaabcbcbcbbcbbc " ) . rstrip ( " qwcbjkl " ) = = " aaa " ) ;
// strip empty string
STRIP_TEST ( String ( " " ) . lstrip ( " qwjkl " ) = = " " ) ;
STRIP_TEST ( String ( " " ) . rstrip ( " qwjkl " ) = = " " ) ;
// strip to empty string
STRIP_TEST ( String ( " abcabcabc " ) . lstrip ( " qwbcajkl " ) = = " " ) ;
STRIP_TEST ( String ( " abcabcabc " ) . rstrip ( " qwbcajkl " ) = = " " ) ;
// don't strip wrong end
STRIP_TEST ( String ( " abc " ) . lstrip ( " qwcjkl " ) = = " abc " ) ;
STRIP_TEST ( String ( " abca " ) . lstrip ( " qwajkl " ) = = " bca " ) ;
STRIP_TEST ( String ( " abc " ) . rstrip ( " qwajkl " ) = = " abc " ) ;
STRIP_TEST ( String ( " abca " ) . rstrip ( " qwajkl " ) = = " abc " ) ;
// in utf-8 "¿" (\u00bf) has the same first byte as "µ" (\u00b5)
// and the same second as "ÿ" (\u00ff)
STRIP_TEST ( String : : utf8 ( " ¿ " ) . lstrip ( String : : utf8 ( " qwaµÿjkl " ) ) = = String : : utf8 ( " ¿ " ) ) ;
STRIP_TEST ( String : : utf8 ( " ¿ " ) . rstrip ( String : : utf8 ( " qwaµÿjkl " ) ) = = String : : utf8 ( " ¿ " ) ) ;
STRIP_TEST ( String : : utf8 ( " µ¿ÿ " ) . lstrip ( String : : utf8 ( " qwaµÿjkl " ) ) = = String : : utf8 ( " ¿ÿ " ) ) ;
STRIP_TEST ( String : : utf8 ( " µ¿ÿ " ) . rstrip ( String : : utf8 ( " qwaµÿjkl " ) ) = = String : : utf8 ( " µ¿ " ) ) ;
CHECK ( state ) ;
# undef STRIP_TEST
}
2022-07-05 14:18:29 +02:00
TEST_CASE ( " [String] Ensuring empty string into parse_utf8 passes empty string " ) {
2020-07-20 18:35:34 +02:00
String empty ;
2022-07-05 14:18:29 +02:00
CHECK ( empty . parse_utf8 ( nullptr , - 1 ) = = ERR_INVALID_DATA ) ;
2020-07-20 18:35:34 +02:00
}
TEST_CASE ( " [String] Cyrillic to_lower() " ) {
String upper = String : : utf8 ( " АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ " ) ;
String lower = String : : utf8 ( " абвгдеёжзийклмнопрстуфхцчшщъыьэюя " ) ;
String test = upper . to_lower ( ) ;
bool state = test = = lower ;
CHECK ( state ) ;
}
TEST_CASE ( " [String] Count and countn functionality " ) {
# define COUNT_TEST(x) \
{ \
bool success = x ; \
state = state & & success ; \
}
bool state = true ;
COUNT_TEST ( String ( " " ) . count ( " Test " ) = = 0 ) ;
COUNT_TEST ( String ( " Test " ) . count ( " " ) = = 0 ) ;
COUNT_TEST ( String ( " Test " ) . count ( " test " ) = = 0 ) ;
COUNT_TEST ( String ( " Test " ) . count ( " TEST " ) = = 0 ) ;
COUNT_TEST ( String ( " TEST " ) . count ( " TEST " ) = = 1 ) ;
COUNT_TEST ( String ( " Test " ) . count ( " Test " ) = = 1 ) ;
COUNT_TEST ( String ( " aTest " ) . count ( " Test " ) = = 1 ) ;
COUNT_TEST ( String ( " Testa " ) . count ( " Test " ) = = 1 ) ;
COUNT_TEST ( String ( " TestTestTest " ) . count ( " Test " ) = = 3 ) ;
COUNT_TEST ( String ( " TestTestTest " ) . count ( " TestTest " ) = = 1 ) ;
COUNT_TEST ( String ( " TestGodotTestGodotTestGodot " ) . count ( " Test " ) = = 3 ) ;
COUNT_TEST ( String ( " TestTestTestTest " ) . count ( " Test " , 4 , 8 ) = = 1 ) ;
COUNT_TEST ( String ( " TestTestTestTest " ) . count ( " Test " , 4 , 12 ) = = 2 ) ;
COUNT_TEST ( String ( " TestTestTestTest " ) . count ( " Test " , 4 , 16 ) = = 3 ) ;
COUNT_TEST ( String ( " TestTestTestTest " ) . count ( " Test " , 4 ) = = 3 ) ;
COUNT_TEST ( String ( " Test " ) . countn ( " test " ) = = 1 ) ;
COUNT_TEST ( String ( " Test " ) . countn ( " TEST " ) = = 1 ) ;
COUNT_TEST ( String ( " testTest-Testatest " ) . countn ( " tEst " ) = = 4 ) ;
COUNT_TEST ( String ( " testTest-TeStatest " ) . countn ( " tEsT " , 4 , 16 ) = = 2 ) ;
CHECK ( state ) ;
2020-07-27 12:43:20 +02:00
# undef COUNT_TEST
}
TEST_CASE ( " [String] Bigrams " ) {
String s = " abcd " ;
Vector < String > bigr = s . bigrams ( ) ;
CHECK ( bigr . size ( ) = = 3 ) ;
CHECK ( bigr [ 0 ] = = " ab " ) ;
CHECK ( bigr [ 1 ] = = " bc " ) ;
CHECK ( bigr [ 2 ] = = " cd " ) ;
}
TEST_CASE ( " [String] c-escape/unescape " ) {
String s = " \\ 1 \a 2 \b \f 3 \n 45 \r 6 \t 7 \v 8 \' 9 \ ?0 \" " ;
CHECK ( s . c_escape ( ) . c_unescape ( ) = = s ) ;
}
2021-12-15 16:01:06 +01:00
TEST_CASE ( " [String] indent " ) {
static const char * input [ ] = {
" " ,
" aaa \n bbb " ,
" \t contains \n \t indent " ,
" empty \n \n line " ,
} ;
static const char * expected [ ] = {
" " ,
" \t aaa \n \t bbb " ,
" \t \t contains \n \t \t indent " ,
" \t empty \n \n \t line " ,
} ;
for ( int i = 0 ; i < 3 ; i + + ) {
CHECK ( String ( input [ i ] ) . indent ( " \t " ) = = expected [ i ] ) ;
}
}
2020-07-27 12:43:20 +02:00
TEST_CASE ( " [String] dedent " ) {
String s = " aaa \n bbb " ;
String t = " aaa \n bbb " ;
CHECK ( s . dedent ( ) = = t ) ;
}
TEST_CASE ( " [String] Path functions " ) {
2022-10-11 14:40:12 +02:00
static const char * path [ 8 ] = { " C: \\ Godot \\ project \\ test.tscn " , " /Godot/project/test.xscn " , " ../Godot/project/test.scn " , " Godot \\ test.doc " , " C: \\ test. " , " res://test " , " user://test " , " /.test " } ;
static const char * base_dir [ 8 ] = { " C: \\ Godot \\ project " , " /Godot/project " , " ../Godot/project " , " Godot " , " C: \\ " , " res:// " , " user:// " , " / " } ;
static const char * base_name [ 8 ] = { " C: \\ Godot \\ project \\ test " , " /Godot/project/test " , " ../Godot/project/test " , " Godot \\ test " , " C: \\ test " , " res://test " , " user://test " , " / " } ;
static const char * ext [ 8 ] = { " tscn " , " xscn " , " scn " , " doc " , " " , " " , " " , " test " } ;
static const char * file [ 8 ] = { " test.tscn " , " test.xscn " , " test.scn " , " test.doc " , " test. " , " test " , " test " , " .test " } ;
static const char * simplified [ 8 ] = { " C:/Godot/project/test.tscn " , " /Godot/project/test.xscn " , " Godot/project/test.scn " , " Godot/test.doc " , " C:/test. " , " res://test " , " user://test " , " /.test " } ;
static const bool abs [ 8 ] = { true , true , false , false , true , true , true , true } ;
for ( int i = 0 ; i < 8 ; i + + ) {
2020-07-27 12:43:20 +02:00
CHECK ( String ( path [ i ] ) . get_base_dir ( ) = = base_dir [ i ] ) ;
CHECK ( String ( path [ i ] ) . get_basename ( ) = = base_name [ i ] ) ;
CHECK ( String ( path [ i ] ) . get_extension ( ) = = ext [ i ] ) ;
CHECK ( String ( path [ i ] ) . get_file ( ) = = file [ i ] ) ;
2021-06-03 15:41:22 +02:00
CHECK ( String ( path [ i ] ) . is_absolute_path ( ) = = abs [ i ] ) ;
2021-08-30 01:43:47 +02:00
CHECK ( String ( path [ i ] ) . is_relative_path ( ) ! = abs [ i ] ) ;
2022-10-11 14:40:12 +02:00
CHECK ( String ( path [ i ] ) . simplify_path ( ) = = String ( simplified [ i ] ) ) ;
2022-08-30 02:34:01 +02:00
CHECK ( String ( path [ i ] ) . simplify_path ( ) . get_base_dir ( ) . path_join ( file [ i ] ) = = String ( path [ i ] ) . simplify_path ( ) ) ;
2020-07-27 12:43:20 +02:00
}
static const char * file_name [ 3 ] = { " test.tscn " , " test://.xscn " , " ?tes*t.scn " } ;
static const bool valid [ 3 ] = { true , false , false } ;
for ( int i = 0 ; i < 3 ; i + + ) {
CHECK ( String ( file_name [ i ] ) . is_valid_filename ( ) = = valid [ i ] ) ;
}
}
TEST_CASE ( " [String] hash " ) {
String a = " Test " ;
String b = " Test " ;
String c = " West " ;
CHECK ( a . hash ( ) = = b . hash ( ) ) ;
CHECK ( a . hash ( ) ! = c . hash ( ) ) ;
CHECK ( a . hash64 ( ) = = b . hash64 ( ) ) ;
CHECK ( a . hash64 ( ) ! = c . hash64 ( ) ) ;
}
2020-11-30 04:43:38 +01:00
TEST_CASE ( " [String] uri_encode/unescape " ) {
2020-07-27 12:43:20 +02:00
String s = " Godot Engine:'docs' " ;
String t = " Godot%20Engine%3A%27docs%27 " ;
2021-04-30 20:22:39 +02:00
String x1 = " T%C4%93%C5%A1t " ;
static const uint8_t u8str [ ] = { 0x54 , 0xC4 , 0x93 , 0xC5 , 0xA1 , 0x74 , 0x00 } ;
String x2 = String : : utf8 ( ( const char * ) u8str ) ;
String x3 = U " Tēšt " ;
CHECK ( x1 . uri_decode ( ) = = x2 ) ;
CHECK ( x1 . uri_decode ( ) = = x3 ) ;
CHECK ( ( x1 + x3 ) . uri_decode ( ) = = ( x2 + x3 ) ) ; // Mixed unicode and URL encoded string, e.g. GTK+ bookmark.
CHECK ( x2 . uri_encode ( ) = = x1 ) ;
CHECK ( x3 . uri_encode ( ) = = x1 ) ;
2020-11-30 04:43:38 +01:00
CHECK ( s . uri_encode ( ) = = t ) ;
CHECK ( t . uri_decode ( ) = = s ) ;
2020-07-27 12:43:20 +02:00
}
TEST_CASE ( " [String] xml_escape/unescape " ) {
String s = " \" Test \" <test@test&'test'> " ;
CHECK ( s . xml_escape ( true ) . xml_unescape ( ) = = s ) ;
CHECK ( s . xml_escape ( false ) . xml_unescape ( ) = = s ) ;
}
2021-02-11 16:33:55 +01:00
TEST_CASE ( " [String] xml_unescape " ) {
// Named entities
String input = " "&'<> " ;
CHECK ( input . xml_unescape ( ) = = " \" & \' <> " ) ;
// Numeric entities
input = " AB " ;
CHECK ( input . xml_unescape ( ) = = " AB " ) ;
input = " �&x#0;More text " ;
String result = input . xml_unescape ( ) ;
// Didn't put in a leading NUL and terminate the string
CHECK ( input . length ( ) > 0 ) ;
CHECK ( input [ 0 ] ! = ' \0 ' ) ;
// Entity should be left as-is if invalid
CHECK ( input . xml_unescape ( ) = = input ) ;
// Check near char32_t range
input = " � " ;
result = input . xml_unescape ( ) ;
CHECK ( result . length ( ) = = 1 ) ;
CHECK ( result [ 0 ] = = 0xFFFFFFFF ) ;
input = " � " ;
result = input . xml_unescape ( ) ;
CHECK ( result . length ( ) = = 1 ) ;
CHECK ( result [ 0 ] = = 0xFFFFFFFF ) ;
// Check out of range of char32_t
input = " � " ;
CHECK ( input . xml_unescape ( ) = = input ) ;
input = " � " ;
CHECK ( input . xml_unescape ( ) = = input ) ;
// Shouldn't consume without ending in a ';'
input = " B " ;
CHECK ( input . xml_unescape ( ) = = input ) ;
input = " A " ;
CHECK ( input . xml_unescape ( ) = = input ) ;
// Invalid characters should make the entity ignored
input = " ASomeIrrelevantText; " ;
CHECK ( input . xml_unescape ( ) = = input ) ;
input = " BSomeIrrelevantText; " ;
CHECK ( input . xml_unescape ( ) = = input ) ;
}
2020-07-27 12:43:20 +02:00
TEST_CASE ( " [String] Strip escapes " ) {
String s = " \t \t Test Test \r \n Test " ;
CHECK ( s . strip_escapes ( ) = = " Test Test Test " ) ;
2020-07-20 18:35:34 +02:00
}
2020-07-27 12:43:20 +02:00
TEST_CASE ( " [String] Similarity " ) {
String a = " Test " ;
String b = " West " ;
String c = " Toad " ;
CHECK ( a . similarity ( b ) > a . similarity ( c ) ) ;
}
TEST_CASE ( " [String] Strip edges " ) {
String s = " \t Test Test " ;
CHECK ( s . strip_edges ( true , false ) = = " Test Test " ) ;
CHECK ( s . strip_edges ( false , true ) = = " \t Test Test " ) ;
CHECK ( s . strip_edges ( true , true ) = = " Test Test " ) ;
}
TEST_CASE ( " [String] Trim " ) {
String s = " aaaTestbbb " ;
CHECK ( s . trim_prefix ( " aaa " ) = = " Testbbb " ) ;
CHECK ( s . trim_suffix ( " bbb " ) = = " aaaTest " ) ;
CHECK ( s . trim_suffix ( " Test " ) = = s ) ;
}
TEST_CASE ( " [String] Right/Left " ) {
String s = " aaaTestbbb " ;
// ^
2020-02-13 16:42:49 +01:00
CHECK ( s . right ( 6 ) = = " estbbb " ) ;
CHECK ( s . right ( - 6 ) = = " tbbb " ) ;
2020-07-27 12:43:20 +02:00
CHECK ( s . left ( 6 ) = = " aaaTes " ) ;
2020-02-13 16:42:49 +01:00
CHECK ( s . left ( - 6 ) = = " aaaT " ) ;
2020-07-27 12:43:20 +02:00
}
TEST_CASE ( " [String] Repeat " ) {
String s = " abababab " ;
String x = " ab " ;
String t = x . repeat ( 4 ) ;
CHECK ( t = = s ) ;
}
TEST_CASE ( " [String] SHA1/SHA256/MD5 " ) {
String s = " Godot " ;
String sha1 = " a1e91f39b9fce6a9998b14bdbe2aa2b39dc2d201 " ;
static uint8_t sha1_buf [ 20 ] = {
0xA1 , 0xE9 , 0x1F , 0x39 , 0xB9 , 0xFC , 0xE6 , 0xA9 , 0x99 , 0x8B , 0x14 , 0xBD , 0xBE , 0x2A , 0xA2 , 0xB3 ,
0x9D , 0xC2 , 0xD2 , 0x01
} ;
String sha256 = " 2a02b2443f7985d89d09001086ae3dcfa6eb0f55c6ef170715d42328e16e6cb8 " ;
static uint8_t sha256_buf [ 32 ] = {
0x2A , 0x02 , 0xB2 , 0x44 , 0x3F , 0x79 , 0x85 , 0xD8 , 0x9D , 0x09 , 0x00 , 0x10 , 0x86 , 0xAE , 0x3D , 0xCF ,
0xA6 , 0xEB , 0x0F , 0x55 , 0xC6 , 0xEF , 0x17 , 0x07 , 0x15 , 0xD4 , 0x23 , 0x28 , 0xE1 , 0x6E , 0x6C , 0xB8
} ;
String md5 = " 4a336d087aeb0390da10ee2ea7cb87f8 " ;
static uint8_t md5_buf [ 16 ] = {
0x4A , 0x33 , 0x6D , 0x08 , 0x7A , 0xEB , 0x03 , 0x90 , 0xDA , 0x10 , 0xEE , 0x2E , 0xA7 , 0xCB , 0x87 , 0xF8
} ;
PackedByteArray buf = s . sha1_buffer ( ) ;
CHECK ( memcmp ( sha1_buf , buf . ptr ( ) , 20 ) = = 0 ) ;
CHECK ( s . sha1_text ( ) = = sha1 ) ;
buf = s . sha256_buffer ( ) ;
CHECK ( memcmp ( sha256_buf , buf . ptr ( ) , 32 ) = = 0 ) ;
CHECK ( s . sha256_text ( ) = = sha256 ) ;
buf = s . md5_buffer ( ) ;
CHECK ( memcmp ( md5_buf , buf . ptr ( ) , 16 ) = = 0 ) ;
CHECK ( s . md5_text ( ) = = md5 ) ;
}
TEST_CASE ( " [String] Join " ) {
String s = " , " ;
Vector < String > parts ;
parts . push_back ( " One " ) ;
parts . push_back ( " B " ) ;
parts . push_back ( " C " ) ;
String t = s . join ( parts ) ;
CHECK ( t = = " One, B, C " ) ;
}
TEST_CASE ( " [String] Is_* " ) {
static const char * data [ 12 ] = { " -30 " , " 100 " , " 10.1 " , " 10,1 " , " 1e2 " , " 1e-2 " , " 1e2e3 " , " 0xAB " , " AB " , " Test1 " , " 1Test " , " Test*1 " } ;
static bool isnum [ 12 ] = { true , true , true , false , false , false , false , false , false , false , false , false } ;
static bool isint [ 12 ] = { true , true , false , false , false , false , false , false , false , false , false , false } ;
static bool ishex [ 12 ] = { true , true , false , false , true , false , true , false , true , false , false , false } ;
static bool ishex_p [ 12 ] = { false , false , false , false , false , false , false , true , false , false , false , false } ;
static bool isflt [ 12 ] = { true , true , true , false , true , true , false , false , false , false , false , false } ;
static bool isid [ 12 ] = { false , false , false , false , false , false , false , false , true , true , false , false } ;
for ( int i = 0 ; i < 12 ; i + + ) {
String s = String ( data [ i ] ) ;
CHECK ( s . is_numeric ( ) = = isnum [ i ] ) ;
2021-06-16 18:24:34 +02:00
CHECK ( s . is_valid_int ( ) = = isint [ i ] ) ;
2020-07-27 12:43:20 +02:00
CHECK ( s . is_valid_hex_number ( false ) = = ishex [ i ] ) ;
CHECK ( s . is_valid_hex_number ( true ) = = ishex_p [ i ] ) ;
CHECK ( s . is_valid_float ( ) = = isflt [ i ] ) ;
CHECK ( s . is_valid_identifier ( ) = = isid [ i ] ) ;
}
}
TEST_CASE ( " [String] humanize_size " ) {
CHECK ( String : : humanize_size ( 1000 ) = = " 1000 B " ) ;
CHECK ( String : : humanize_size ( 1025 ) = = " 1.00 KiB " ) ;
CHECK ( String : : humanize_size ( 1025300 ) = = " 1001.2 KiB " ) ;
CHECK ( String : : humanize_size ( 100523550 ) = = " 95.86 MiB " ) ;
CHECK ( String : : humanize_size ( 5345555000 ) = = " 4.97 GiB " ) ;
}
2021-01-28 21:48:12 +01:00
TEST_CASE ( " [String] validate_node_name " ) {
String numeric_only = " 12345 " ;
CHECK ( numeric_only . validate_node_name ( ) = = " 12345 " ) ;
String name_with_spaces = " Name with spaces " ;
CHECK ( name_with_spaces . validate_node_name ( ) = = " Name with spaces " ) ;
2022-07-05 14:18:29 +02:00
String name_with_kana = U " Name with kana ゴドツ " ;
CHECK ( name_with_kana . validate_node_name ( ) = = U " Name with kana ゴドツ " ) ;
2021-01-28 21:48:12 +01:00
2023-04-06 17:54:56 +02:00
String name_with_invalid_chars = " Name with invalid characters :.@%removed! " ;
CHECK ( name_with_invalid_chars . validate_node_name ( ) = = " Name with invalid characters ____removed! " ) ;
2021-01-28 21:48:12 +01:00
}
2021-09-17 21:02:21 +02:00
2022-05-02 15:04:17 +02:00
TEST_CASE ( " [String] validate_identifier " ) {
String empty_string ;
CHECK ( empty_string . validate_identifier ( ) = = " _ " ) ;
String numeric_only = " 12345 " ;
CHECK ( numeric_only . validate_identifier ( ) = = " _2345 " ) ;
String name_with_spaces = " Name with spaces " ;
CHECK ( name_with_spaces . validate_identifier ( ) = = " Name_with_spaces " ) ;
String name_with_invalid_chars = String : : utf8 ( " Invalid characters:@*#&世界 " ) ;
CHECK ( name_with_invalid_chars . validate_identifier ( ) = = " Invalid_characters_______ " ) ;
}
2021-09-17 21:02:21 +02:00
TEST_CASE ( " [String] Variant indexed get " ) {
Variant s = String ( " abcd " ) ;
bool valid = false ;
bool oob = true ;
String r = s . get_indexed ( 1 , valid , oob ) ;
CHECK ( valid ) ;
CHECK_FALSE ( oob ) ;
CHECK_EQ ( r , String ( " b " ) ) ;
}
TEST_CASE ( " [String] Variant validated indexed get " ) {
Variant s = String ( " abcd " ) ;
Variant : : ValidatedIndexedGetter getter = Variant : : get_member_validated_indexed_getter ( Variant : : STRING ) ;
Variant r ;
bool oob = true ;
getter ( & s , 1 , & r , & oob ) ;
CHECK_FALSE ( oob ) ;
CHECK_EQ ( r , String ( " b " ) ) ;
}
TEST_CASE ( " [String] Variant ptr indexed get " ) {
String s ( " abcd " ) ;
Variant : : PTRIndexedGetter getter = Variant : : get_member_ptr_indexed_getter ( Variant : : STRING ) ;
String r ;
getter ( & s , 1 , & r ) ;
CHECK_EQ ( r , String ( " b " ) ) ;
}
TEST_CASE ( " [String] Variant indexed set " ) {
Variant s = String ( " abcd " ) ;
bool valid = false ;
bool oob = true ;
s . set_indexed ( 1 , String ( " z " ) , valid , oob ) ;
CHECK ( valid ) ;
CHECK_FALSE ( oob ) ;
CHECK_EQ ( s , String ( " azcd " ) ) ;
}
TEST_CASE ( " [String] Variant validated indexed set " ) {
Variant s = String ( " abcd " ) ;
Variant : : ValidatedIndexedSetter setter = Variant : : get_member_validated_indexed_setter ( Variant : : STRING ) ;
Variant v = String ( " z " ) ;
bool oob = true ;
setter ( & s , 1 , & v , & oob ) ;
CHECK_FALSE ( oob ) ;
CHECK_EQ ( s , String ( " azcd " ) ) ;
}
TEST_CASE ( " [String] Variant ptr indexed set " ) {
String s ( " abcd " ) ;
Variant : : PTRIndexedSetter setter = Variant : : get_member_ptr_indexed_setter ( Variant : : STRING ) ;
String v ( " z " ) ;
setter ( & s , 1 , & v ) ;
CHECK_EQ ( s , String ( " azcd " ) ) ;
}
2021-12-09 10:42:46 +01:00
TEST_CASE ( " [Stress][String] Empty via ' == String()' " ) {
for ( int i = 0 ; i < 100000 ; + + i ) {
String str = " Hello World! " ;
2022-08-30 11:36:24 +02:00
if ( str = = String ( ) ) {
2021-12-09 10:42:46 +01:00
continue ;
}
}
}
TEST_CASE ( " [Stress][String] Empty via `is_empty()` " ) {
for ( int i = 0 ; i < 100000 ; + + i ) {
String str = " Hello World! " ;
if ( str . is_empty ( ) ) {
continue ;
}
}
}
2020-07-20 18:35:34 +02:00
} // namespace TestString
# endif // TEST_STRING_H