fontfeaturesettings_test.cpp (12681B)
1 // This file is part of Blend2D project <https://blend2d.com> 2 // 3 // See blend2d.h or LICENSE.md for license and copyright information 4 // SPDX-License-Identifier: Zlib 5 6 #include "api-build_test_p.h" 7 #if defined(BL_TEST) 8 9 #include "array_p.h" 10 #include "fontfeaturesettings_p.h" 11 #include "fonttagdata_p.h" 12 #include "object_p.h" 13 14 // bl::FontFeatureSettings - Tests 15 // =============================== 16 17 namespace bl { 18 namespace Tests { 19 20 static void verify_font_feature_settings(const BLFontFeatureSettings& ffs) noexcept { 21 BLFontFeatureSettingsView view; 22 ffs.get_view(&view); 23 24 if (view.size == 0) 25 return; 26 27 uint32_t prev_tag = view.data[0].tag; 28 for (size_t i = 1; i < view.size; i++) { 29 EXPECT_LT(prev_tag, view.data[i].tag) 30 .message("BLFontFeatureSettings is corrupted - previous tag 0x%08X is not less than current tag 0x%08X at [%zu]", prev_tag, view.data[i].tag, i); 31 prev_tag = view.data[i].tag; 32 } 33 } 34 35 UNIT(fontfeaturesettings_allocation_strategy, BL_TEST_GROUP_TEXT_CONTAINERS) { 36 BLFontFeatureSettings ffs; 37 size_t capacity = ffs.capacity(); 38 39 constexpr uint32_t kCharRange = FontTagData::kCharRangeInTag; 40 constexpr uint32_t kNumItems = FontTagData::kUniqueTagCount / 100; 41 42 for (uint32_t i = 0; i < kNumItems; i++) { 43 BLTag tag = BL_MAKE_TAG( 44 uint32_t(' ') + (i / (kCharRange * kCharRange * kCharRange)), 45 uint32_t(' ') + (i / (kCharRange * kCharRange)) % kCharRange, 46 uint32_t(' ') + (i / (kCharRange)) % kCharRange, 47 uint32_t(' ') + (i % kCharRange)); 48 49 ffs.set_value(tag, i & 0xFFFFu); 50 if (capacity != ffs.capacity()) { 51 size_t impl_size = FontFeatureSettingsInternal::impl_size_from_capacity(ffs.capacity()).value(); 52 INFO("Capacity increased from %zu to %zu [ImplSize=%zu]\n", capacity, ffs.capacity(), impl_size); 53 capacity = ffs.capacity(); 54 } 55 } 56 57 verify_font_feature_settings(ffs); 58 } 59 60 UNIT(fontfeaturesettings, BL_TEST_GROUP_TEXT_CONTAINERS) { 61 // These are not sorted on purpose to test whether BLFontFeatureSettings would sort them during insertion. 62 static const uint32_t fat_tags[] = { 63 BL_MAKE_TAG('r', 'a', 'n', 'd'), 64 BL_MAKE_TAG('a', 'a', 'l', 't'), 65 BL_MAKE_TAG('s', 's', '0', '9'), 66 BL_MAKE_TAG('s', 's', '0', '4') 67 }; 68 69 INFO("SSO initial state"); 70 { 71 BLFontFeatureSettings ffs; 72 73 EXPECT_TRUE(ffs._d.sso()); 74 EXPECT_TRUE(ffs.is_empty()); 75 EXPECT_EQ(ffs.size(), 0u); 76 EXPECT_EQ(ffs.capacity(), BLFontFeatureSettings::kSSOCapacity); 77 78 // SSO mode should present all available features as invalid (unassigned). 79 for (uint32_t feature_id = 0; feature_id < FontTagData::kFeatureIdCount; feature_id++) { 80 BLTag feature_tag = FontTagData::feature_id_to_tag_table[feature_id]; 81 EXPECT_EQ(ffs.get_value(feature_tag), BL_FONT_FEATURE_INVALID_VALUE); 82 } 83 84 // Trying to get an unknown tag should fail. 85 EXPECT_EQ(ffs.get_value(BL_MAKE_TAG('-', '-', '-', '-')), BL_FONT_FEATURE_INVALID_VALUE); 86 EXPECT_EQ(ffs.get_value(BL_MAKE_TAG('a', 'a', 'a', 'a')), BL_FONT_FEATURE_INVALID_VALUE); 87 EXPECT_EQ(ffs.get_value(BL_MAKE_TAG('z', 'z', 'z', 'z')), BL_FONT_FEATURE_INVALID_VALUE); 88 } 89 90 INFO("SSO bit tag/value storage"); 91 { 92 BLFontFeatureSettings ffs; 93 94 // SSO storage must allow to store ALL font features that have bit mapping. 95 uint32_t num_tags = 0; 96 for (uint32_t feature_id = 0; feature_id < FontTagData::kFeatureIdCount; feature_id++) { 97 if (FontTagData::feature_info_table[feature_id].has_bit_id()) { 98 num_tags++; 99 BLTag feature_tag = FontTagData::feature_id_to_tag_table[feature_id]; 100 101 EXPECT_SUCCESS(ffs.set_value(feature_tag, 1u)); 102 EXPECT_EQ(ffs.get_value(feature_tag), 1u); 103 EXPECT_EQ(ffs.size(), num_tags); 104 EXPECT_TRUE(ffs._d.sso()); 105 106 verify_font_feature_settings(ffs); 107 } 108 } 109 110 // Set all features to zero (disabled, but still present in the mapping). 111 for (uint32_t feature_id = 0; feature_id < FontTagData::kFeatureIdCount; feature_id++) { 112 if (FontTagData::feature_info_table[feature_id].has_bit_id()) { 113 BLTag feature_tag = FontTagData::feature_id_to_tag_table[feature_id]; 114 115 EXPECT_SUCCESS(ffs.set_value(feature_tag, 0u)); 116 EXPECT_EQ(ffs.get_value(feature_tag), 0u); 117 EXPECT_EQ(ffs.size(), num_tags); 118 EXPECT_TRUE(ffs._d.sso()); 119 verify_font_feature_settings(ffs); 120 } 121 } 122 123 // Remove all features. 124 for (uint32_t feature_id = 0; feature_id < FontTagData::kFeatureIdCount; feature_id++) { 125 if (FontTagData::feature_info_table[feature_id].has_bit_id()) { 126 num_tags--; 127 BLTag feature_tag = FontTagData::feature_id_to_tag_table[feature_id]; 128 129 EXPECT_SUCCESS(ffs.remove_value(feature_tag)); 130 EXPECT_EQ(ffs.get_value(feature_tag), BL_FONT_FEATURE_INVALID_VALUE); 131 EXPECT_EQ(ffs.size(), num_tags); 132 EXPECT_TRUE(ffs._d.sso()); 133 134 verify_font_feature_settings(ffs); 135 } 136 } 137 138 EXPECT_TRUE(ffs.is_empty()); 139 EXPECT_EQ(ffs, BLFontFeatureSettings()); 140 } 141 142 INFO("SSO bit tag/value storage limitations"); 143 { 144 BLFontFeatureSettings ffs; 145 146 // Trying to set any other value than 0-1 with bit tags fails. 147 for (uint32_t feature_id = 0; feature_id < FontTagData::kFeatureIdCount; feature_id++) { 148 if (FontTagData::feature_info_table[feature_id].has_bit_id()) { 149 BLTag feature_tag = FontTagData::feature_id_to_tag_table[feature_id]; 150 EXPECT_EQ(ffs.set_value(feature_tag, 2u), BL_ERROR_INVALID_VALUE); 151 } 152 } 153 154 EXPECT_TRUE(ffs.is_empty()); 155 } 156 157 INFO("SSO bit tag/value storage + fat tag/value storage"); 158 { 159 BLFontFeatureSettings ffs; 160 uint32_t num_tags = 0; 161 162 // Add fat tag/value data. 163 for (uint32_t i = 0; i < BL_ARRAY_SIZE(fat_tags); i++) { 164 num_tags++; 165 166 EXPECT_SUCCESS(ffs.set_value(fat_tags[i], 15u)); 167 EXPECT_EQ(ffs.get_value(fat_tags[i]), 15u); 168 EXPECT_EQ(ffs.size(), num_tags); 169 EXPECT_TRUE(ffs._d.sso()); 170 171 verify_font_feature_settings(ffs); 172 173 // Verify that changing a fat tag's value is working properly (it's bit twiddling). 174 EXPECT_SUCCESS(ffs.set_value(fat_tags[i], 1u)); 175 EXPECT_EQ(ffs.get_value(fat_tags[i]), 1u); 176 EXPECT_EQ(ffs.size(), num_tags); 177 EXPECT_TRUE(ffs._d.sso()); 178 179 verify_font_feature_settings(ffs); 180 } 181 182 // Add bit tag/value data. 183 for (uint32_t feature_id = 0; feature_id < FontTagData::kFeatureIdCount; feature_id++) { 184 if (FontTagData::feature_info_table[feature_id].has_bit_id()) { 185 num_tags++; 186 BLTag feature_tag = FontTagData::feature_id_to_tag_table[feature_id]; 187 188 EXPECT_SUCCESS(ffs.set_value(feature_tag, 1u)); 189 EXPECT_EQ(ffs.get_value(feature_tag), 1u); 190 EXPECT_EQ(ffs.size(), num_tags); 191 EXPECT_TRUE(ffs._d.sso()); 192 193 verify_font_feature_settings(ffs); 194 } 195 } 196 197 // Remove fat tag/value data. 198 for (uint32_t i = 0; i < BL_ARRAY_SIZE(fat_tags); i++) { 199 num_tags--; 200 201 EXPECT_SUCCESS(ffs.remove_value(fat_tags[i])); 202 EXPECT_EQ(ffs.size(), num_tags); 203 EXPECT_TRUE(ffs._d.sso()); 204 205 verify_font_feature_settings(ffs); 206 } 207 208 // Remove bit tag/value data. 209 for (uint32_t feature_id = 0; feature_id < FontTagData::kFeatureIdCount; feature_id++) { 210 if (FontTagData::feature_info_table[feature_id].has_bit_id()) { 211 num_tags--; 212 BLTag feature_tag = FontTagData::feature_id_to_tag_table[feature_id]; 213 214 EXPECT_SUCCESS(ffs.remove_value(feature_tag)); 215 EXPECT_EQ(ffs.size(), num_tags); 216 EXPECT_TRUE(ffs._d.sso()); 217 218 verify_font_feature_settings(ffs); 219 } 220 } 221 222 EXPECT_TRUE(ffs.is_empty()); 223 EXPECT_EQ(ffs, BLFontFeatureSettings()); 224 } 225 226 INFO("SSO tag/value equality"); 227 { 228 BLFontFeatureSettings ffsA; 229 BLFontFeatureSettings ffsB; 230 231 // Assign bit tag/value data. 232 for (uint32_t i = 0; i < FontTagData::kFeatureIdCount; i++) { 233 uint32_t feature_id = i; 234 if (FontTagData::feature_info_table[feature_id].has_bit_id()) { 235 BLTag feature_tag = FontTagData::feature_id_to_tag_table[feature_id]; 236 EXPECT_SUCCESS(ffsA.set_value(feature_tag, 1u)); 237 verify_font_feature_settings(ffsA); 238 } 239 } 240 241 for (uint32_t i = 0; i < FontTagData::kFeatureIdCount; i++) { 242 uint32_t feature_id = FontTagData::kFeatureIdCount - 1 - i; 243 if (FontTagData::feature_info_table[feature_id].has_bit_id()) { 244 BLTag feature_tag = FontTagData::feature_id_to_tag_table[feature_id]; 245 EXPECT_SUCCESS(ffsB.set_value(feature_tag, 1u)); 246 verify_font_feature_settings(ffsB); 247 } 248 } 249 250 EXPECT_EQ(ffsA, ffsB); 251 252 // Assign fat tag/value data. 253 for (uint32_t i = 0; i < BL_ARRAY_SIZE(fat_tags); i++) { 254 EXPECT_SUCCESS(ffsA.set_value(fat_tags[i], i)); 255 verify_font_feature_settings(ffsA); 256 } 257 258 for (uint32_t iRev = 0; iRev < BL_ARRAY_SIZE(fat_tags); iRev++) { 259 uint32_t i = BL_ARRAY_SIZE(fat_tags) - 1 - iRev; 260 EXPECT_SUCCESS(ffsB.set_value(fat_tags[i], i)); 261 verify_font_feature_settings(ffsB); 262 } 263 264 EXPECT_EQ(ffsA, ffsB); 265 266 // Remove fat tag/value data. 267 for (uint32_t i = 0; i < BL_ARRAY_SIZE(fat_tags); i++) { 268 EXPECT_SUCCESS(ffsA.remove_value(fat_tags[i])); 269 verify_font_feature_settings(ffsA); 270 } 271 272 for (uint32_t iRev = 0; iRev < BL_ARRAY_SIZE(fat_tags); iRev++) { 273 uint32_t i = BL_ARRAY_SIZE(fat_tags) - 1 - iRev; 274 EXPECT_SUCCESS(ffsB.remove_value(fat_tags[i])); 275 verify_font_feature_settings(ffsB); 276 } 277 278 EXPECT_EQ(ffsA, ffsB); 279 } 280 281 INFO("Dynamic representation"); 282 { 283 BLFontFeatureSettings ffs; 284 285 for (uint32_t i = 0; i < FontTagData::kFeatureIdCount; i++) { 286 uint32_t feature_id = FontTagData::kFeatureIdCount - 1 - i; 287 BLTag feature_tag = FontTagData::feature_id_to_tag_table[feature_id]; 288 EXPECT_SUCCESS(ffs.set_value(feature_tag, 1u)); 289 EXPECT_EQ(ffs.get_value(feature_tag), 1u); 290 EXPECT_EQ(ffs.size(), i + 1u); 291 verify_font_feature_settings(ffs); 292 } 293 294 EXPECT_FALSE(ffs._d.sso()); 295 296 for (uint32_t i = 0; i < FontTagData::kFeatureIdCount; i++) { 297 uint32_t feature_id = FontTagData::kFeatureIdCount - 1 - i; 298 BLTag feature_tag = FontTagData::feature_id_to_tag_table[feature_id]; 299 300 if (!FontTagData::feature_info_table[feature_id].has_bit_id()) { 301 EXPECT_SUCCESS(ffs.set_value(feature_tag, 65535u)); 302 EXPECT_EQ(ffs.get_value(feature_tag), 65535u); 303 } 304 else { 305 EXPECT_SUCCESS(ffs.set_value(feature_tag, 0u)); 306 EXPECT_EQ(ffs.get_value(feature_tag), 0u); 307 } 308 309 verify_font_feature_settings(ffs); 310 } 311 312 EXPECT_FALSE(ffs._d.sso()); 313 314 for (uint32_t i = 0; i < FontTagData::kFeatureIdCount; i++) { 315 uint32_t feature_id = i; 316 BLTag feature_tag = FontTagData::feature_id_to_tag_table[feature_id]; 317 318 EXPECT_SUCCESS(ffs.remove_value(feature_tag)); 319 EXPECT_EQ(ffs.get_value(feature_tag), BL_FONT_FEATURE_INVALID_VALUE); 320 321 verify_font_feature_settings(ffs); 322 } 323 324 EXPECT_TRUE(ffs.is_empty()); 325 EXPECT_EQ(ffs.size(), 0u); 326 EXPECT_FALSE(ffs._d.sso()); 327 328 } 329 330 INFO("Dynamic tag/value equality"); 331 { 332 BLFontFeatureSettings ffs1; 333 BLFontFeatureSettings ffs2; 334 335 for (uint32_t i = 0; i < FontTagData::kFeatureIdCount; i++) { 336 EXPECT_SUCCESS(ffs1.set_value(FontTagData::feature_id_to_tag_table[i], 1u)); 337 EXPECT_SUCCESS(ffs2.set_value(FontTagData::feature_id_to_tag_table[FontTagData::kFeatureIdCount - 1u - i], 1u)); 338 339 verify_font_feature_settings(ffs1); 340 verify_font_feature_settings(ffs2); 341 } 342 343 EXPECT_EQ(ffs1, ffs2); 344 } 345 346 INFO("Dynamic tag/value vs SSO tag/value equality"); 347 { 348 BLFontFeatureSettings ffs1; 349 BLFontFeatureSettings ffs2; 350 351 for (uint32_t i = 0; i < FontTagData::kFeatureIdCount; i++) { 352 if (FontTagData::feature_info_table[i].has_bit_id()) { 353 EXPECT_SUCCESS(ffs1.set_value(FontTagData::feature_id_to_tag_table[i], 1u)); 354 EXPECT_SUCCESS(ffs2.set_value(FontTagData::feature_id_to_tag_table[i], 1u)); 355 356 verify_font_feature_settings(ffs1); 357 verify_font_feature_settings(ffs2); 358 } 359 } 360 361 EXPECT_EQ(ffs1, ffs2); 362 363 // Make ffs1 go out of SSO mode. 364 EXPECT_SUCCESS(ffs1.set_value(BL_MAKE_TAG('a', 'a', 'a', 'a'), 1000)); 365 EXPECT_SUCCESS(ffs1.remove_value(BL_MAKE_TAG('a', 'a', 'a', 'a'))); 366 EXPECT_EQ(ffs1, ffs2); 367 EXPECT_EQ(ffs2, ffs1); 368 369 // Make ffs2 go out of SSO mode. 370 EXPECT_SUCCESS(ffs2.set_value(BL_MAKE_TAG('a', 'a', 'a', 'a'), 1000)); 371 EXPECT_SUCCESS(ffs2.remove_value(BL_MAKE_TAG('a', 'a', 'a', 'a'))); 372 EXPECT_EQ(ffs1, ffs2); 373 EXPECT_EQ(ffs2, ffs1); 374 } 375 } 376 377 } // {Tests} 378 } // {bl} 379 380 #endif // BL_TEST