1/* 2 * Copyright © 2011 Google, Inc. 3 * 4 * This is part of HarfBuzz, a text shaping library. 5 * 6 * Permission is hereby granted, without written agreement and without 7 * license or royalty fees, to use, copy, modify, and distribute this 8 * software and its documentation for any purpose, provided that the 9 * above copyright notice and the following two paragraphs appear in 10 * all copies of this software. 11 * 12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR 13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 14 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN 15 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 16 * DAMAGE. 17 * 18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, 19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 20 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS 21 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO 22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 23 * 24 * Google Author(s): Behdad Esfahbod 25 */ 26 27#include "hb-test.h" 28 29/* Unit tests for hb-blob.h */ 30 31#if defined(HAVE_SYS_MMAN_H) && defined(HAVE_MPROTECT) && defined(HAVE_MMAP) 32 33# define TEST_MMAP 1 34 35#ifdef HAVE_SYS_MMAN_H 36#ifdef HAVE_UNISTD_H 37#include <unistd.h> 38#endif /* HAVE_UNISTD_H */ 39#include <sys/mman.h> 40#endif /* HAVE_SYS_MMAN_H */ 41 42#endif 43 44 45static void 46test_blob_empty (void) 47{ 48 hb_blob_t *blob; 49 unsigned int len; 50 const char *data; 51 char *data_writable; 52 53 g_assert (hb_blob_is_immutable (hb_blob_get_empty ())); 54 g_assert (hb_blob_get_empty () != NULL); 55 g_assert (hb_blob_get_empty () == hb_blob_create (NULL, 0, HB_MEMORY_MODE_READONLY, NULL, NULL)); 56 g_assert (hb_blob_get_empty () == hb_blob_create ("asdf", 0, HB_MEMORY_MODE_READONLY, NULL, NULL)); 57 g_assert (hb_blob_get_empty () == hb_blob_create (NULL, -1, HB_MEMORY_MODE_READONLY, NULL, NULL)); 58 g_assert (hb_blob_get_empty () == hb_blob_create ("asdfg", -1, HB_MEMORY_MODE_READONLY, NULL, NULL)); 59 60 blob = hb_blob_get_empty (); 61 g_assert (blob == hb_blob_get_empty ()); 62 63 len = hb_blob_get_length (blob); 64 g_assert_cmpint (len, ==, 0); 65 66 data = hb_blob_get_data (blob, NULL); 67 g_assert (data == NULL); 68 69 data = hb_blob_get_data (blob, &len); 70 g_assert (data == NULL); 71 g_assert_cmpint (len, ==, 0); 72 73 data_writable = hb_blob_get_data_writable (blob, NULL); 74 g_assert (data_writable == NULL); 75 76 data_writable = hb_blob_get_data_writable (blob, &len); 77 g_assert (data_writable == NULL); 78 g_assert_cmpint (len, ==, 0); 79} 80 81static const char test_data[] = "test\0data"; 82 83static const char *blob_names[] = { 84 "duplicate", 85 "readonly", 86 "writable" 87#ifdef TEST_MMAP 88 , "readonly-may-make-writable" 89#endif 90}; 91 92typedef struct 93{ 94 hb_blob_t *blob; 95 int freed; 96 char *data; 97 unsigned int len; 98} fixture_t; 99 100static void 101free_up (fixture_t *fixture) 102{ 103 g_assert_cmpint (fixture->freed, ==, 0); 104 fixture->freed++; 105} 106 107static void 108free_up_free (fixture_t *fixture) 109{ 110 free_up (fixture); 111 free (fixture->data); 112} 113 114 115#ifdef TEST_MMAP 116static uintptr_t 117get_pagesize (void) 118{ 119 uintptr_t pagesize = -1; 120 121#if defined(HAVE_SYSCONF) && defined(_SC_PAGE_SIZE) 122 pagesize = (uintptr_t) sysconf (_SC_PAGE_SIZE); 123#elif defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE) 124 pagesize = (uintptr_t) sysconf (_SC_PAGESIZE); 125#elif defined(HAVE_GETPAGESIZE) 126 pagesize = (uintptr_t) getpagesize (); 127#endif 128 129 g_assert (pagesize != (uintptr_t) -1); 130 131 return pagesize; 132} 133 134static void 135free_up_munmap (fixture_t *fixture) 136{ 137 free_up (fixture); 138 munmap (fixture->data, get_pagesize ()); 139} 140#endif 141 142#include <errno.h> 143static void 144fixture_init (fixture_t *fixture, gconstpointer user_data) 145{ 146 hb_memory_mode_t mm = (hb_memory_mode_t) GPOINTER_TO_INT (user_data); 147 unsigned int len; 148 const char *data; 149 hb_destroy_func_t free_func; 150 151 switch (GPOINTER_TO_INT (user_data)) 152 { 153 case HB_MEMORY_MODE_DUPLICATE: 154 data = test_data; 155 len = sizeof (test_data); 156 free_func = (hb_destroy_func_t) free_up; 157 break; 158 159 case HB_MEMORY_MODE_READONLY: 160 data = test_data; 161 len = sizeof (test_data); 162 free_func = (hb_destroy_func_t) free_up; 163 break; 164 165 case HB_MEMORY_MODE_WRITABLE: 166 data = malloc (sizeof (test_data)); 167 memcpy ((char *) data, test_data, sizeof (test_data)); 168 len = sizeof (test_data); 169 free_func = (hb_destroy_func_t) free_up_free; 170 break; 171 172#ifdef TEST_MMAP 173 case HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE: 174 { 175 uintptr_t pagesize = get_pagesize (); 176 177 data = mmap (NULL, pagesize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); 178 g_assert (data != (char *) -1); 179 memcpy ((char *) data, test_data, sizeof (test_data)); 180 mprotect ((char *) data, pagesize, PROT_READ); 181 len = sizeof (test_data); 182 free_func = (hb_destroy_func_t) free_up_munmap; 183 break; 184 } 185#endif 186 187 default: 188 g_assert_not_reached (); 189 } 190 191 fixture->freed = 0; 192 fixture->data = (char *) data; 193 fixture->len = len; 194 fixture->blob = hb_blob_create (data, len, mm, fixture, free_func); 195} 196 197static void 198fixture_finish (fixture_t *fixture, gconstpointer user_data) 199{ 200 hb_blob_destroy (fixture->blob); 201 g_assert_cmpint (fixture->freed, ==, 1); 202} 203 204 205static void 206test_blob (fixture_t *fixture, gconstpointer user_data) 207{ 208 hb_blob_t *b = fixture->blob; 209 hb_memory_mode_t mm = GPOINTER_TO_INT (user_data); 210 unsigned int len; 211 const char *data; 212 char *data_writable; 213 unsigned int i; 214 215 g_assert (b); 216 217 len = hb_blob_get_length (b); 218 g_assert_cmpint (len, ==, fixture->len); 219 220 data = hb_blob_get_data (b, &len); 221 g_assert_cmpint (len, ==, fixture->len); 222 if (mm == HB_MEMORY_MODE_DUPLICATE) { 223 g_assert (data != fixture->data); 224 g_assert_cmpint (fixture->freed, ==, 1); 225 mm = HB_MEMORY_MODE_WRITABLE; 226 } else { 227 g_assert (data == fixture->data); 228 g_assert_cmpint (fixture->freed, ==, 0); 229 } 230 231 data_writable = hb_blob_get_data_writable (b, &len); 232 g_assert_cmpint (len, ==, fixture->len); 233 g_assert (data_writable); 234 g_assert (0 == memcmp (data_writable, fixture->data, fixture->len)); 235 if (mm == HB_MEMORY_MODE_READONLY) { 236 g_assert (data_writable != data); 237 g_assert_cmpint (fixture->freed, ==, 1); 238 } else { 239 g_assert (data_writable == data); 240 } 241 242 data = hb_blob_get_data (b, &len); 243 g_assert_cmpint (len, ==, fixture->len); 244 g_assert (data == data_writable); 245 246 memset (data_writable, 0, fixture->len); 247 248 /* Now, make it immutable and watch get_data_writable() fail */ 249 250 g_assert (!hb_blob_is_immutable (b)); 251 hb_blob_make_immutable (b); 252 g_assert (hb_blob_is_immutable (b)); 253 254 data_writable = hb_blob_get_data_writable (b, &len); 255 g_assert (!data_writable); 256 g_assert_cmpint (len, ==, 0); 257 258 data = hb_blob_get_data (b, &len); 259 g_assert_cmpint (len, ==, fixture->len); 260 for (i = 0; i < len; i++) 261 g_assert ('\0' == data[i]); 262} 263 264static void 265test_blob_subblob (fixture_t *fixture, gconstpointer user_data) 266{ 267 hb_blob_t *b = fixture->blob; 268 hb_memory_mode_t mm = GPOINTER_TO_INT (user_data); 269 unsigned int len; 270 const char *data; 271 char *data_writable; 272 unsigned int i; 273 274 if (mm == HB_MEMORY_MODE_DUPLICATE) { 275 g_assert_cmpint (fixture->freed, ==, 1); 276 fixture->data = (char *) hb_blob_get_data (b, NULL); 277 } else { 278 g_assert_cmpint (fixture->freed, ==, 0); 279 } 280 fixture->blob = hb_blob_create_sub_blob (b, 1, fixture->len - 2); 281 hb_blob_destroy (b); 282 b = fixture->blob; 283 284 /* A sub-blob is always created READONLY. */ 285 286 g_assert (b); 287 288 len = hb_blob_get_length (b); 289 g_assert_cmpint (len, ==, fixture->len - 2); 290 291 data = hb_blob_get_data (b, &len); 292 g_assert_cmpint (len, ==, fixture->len - 2); 293 g_assert (data == fixture->data + 1); 294 295 data_writable = hb_blob_get_data_writable (b, &len); 296 g_assert_cmpint (len, ==, fixture->len - 2); 297 g_assert (data_writable); 298 if (mm == HB_MEMORY_MODE_READONLY) 299 g_assert (0 == memcmp (data_writable, fixture->data + 1, fixture->len - 2)); 300 g_assert (data_writable != data); 301 g_assert_cmpint (fixture->freed, ==, 1); 302 303 data = hb_blob_get_data (b, &len); 304 g_assert_cmpint (len, ==, fixture->len - 2); 305 g_assert (data == data_writable); 306 307 memset (data_writable, 0, fixture->len - 2); 308 309 /* Now, make it immutable and watch get_data_writable() fail */ 310 311 g_assert (!hb_blob_is_immutable (b)); 312 hb_blob_make_immutable (b); 313 g_assert (hb_blob_is_immutable (b)); 314 315 data_writable = hb_blob_get_data_writable (b, &len); 316 g_assert (!data_writable); 317 g_assert_cmpint (len, ==, 0); 318 319 data = hb_blob_get_data (b, &len); 320 g_assert_cmpint (len, ==, fixture->len - 2); 321 for (i = 0; i < len; i++) 322 g_assert ('\0' == data[i]); 323} 324 325 326int 327main (int argc, char **argv) 328{ 329 unsigned int i; 330 331 hb_test_init (&argc, &argv); 332 333 hb_test_add (test_blob_empty); 334 335 for (i = 0; i < G_N_ELEMENTS (blob_names); i++) 336 { 337 const void *blob_type = GINT_TO_POINTER (i); 338 const char *blob_name = blob_names[i]; 339 340 hb_test_add_fixture_flavor (fixture, blob_type, blob_name, test_blob); 341 hb_test_add_fixture_flavor (fixture, blob_type, blob_name, test_blob_subblob); 342 } 343 344 /* 345 * create_sub_blob 346 */ 347 348 return hb_test_run (); 349} 350