Files
ciciplusplus dcc056a75f Do not run test_read_directory_as_fd on macOS itself
Change-Id: I9bd8ad8f65263abb983c5a369867c76663766c21
2026-01-26 00:32:41 +01:00

3225 lines
84 KiB
Objective-C

/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
// This file contains the command-line automated tests. tests/integration.rs
// runs these automatically.
#include <CoreFoundation/CFBase.h>
#include <CoreFoundation/CFBundle.h>
#include <CoreFoundation/CFDictionary.h>
#include <CoreFoundation/CFNumber.h>
#include <CoreFoundation/CFString.h>
#include <CoreFoundation/CFURL.h>
#include <arpa/inet.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <fenv.h>
#include <locale.h>
#include <math.h>
#include <pthread.h>
#include <semaphore.h>
#include <setjmp.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <wchar.h>
#import "SyncTester.h"
// Declare test functions from other files.
int test_AutoreleasePool(void); // AutoReleasePoolTest.m
int test_CGAffineTransform(void); // CGAffineTransform.c
// === Main code ===
int test_CGGeometry() {
CGRect testRect;
testRect.origin.x = 2.0;
testRect.origin.y = 3.0;
testRect.size.width = 100.0;
testRect.size.height = 200.0;
if (!(CGRectGetMinX(testRect) == testRect.origin.x &&
CGRectGetMinX(testRect) == 2.0))
return -1;
if (!(CGRectGetMaxX(testRect) == testRect.origin.x + testRect.size.width &&
CGRectGetMaxX(testRect) == 102.0))
return -2;
if (!(CGRectGetMinY(testRect) == testRect.origin.y &&
CGRectGetMinY(testRect) == 3.0))
return -3;
if (!(CGRectGetMaxY(testRect) == testRect.origin.y + testRect.size.height &&
CGRectGetMaxY(testRect) == 203.0))
return -4;
if (!(CGRectGetHeight(testRect) == testRect.size.height))
return -5;
if (!(CGRectGetWidth(testRect) == testRect.size.width))
return -6;
return 0;
}
int int_compar(const void *a, const void *b) { return *(int *)a - *(int *)b; }
int sort_and_check(int nel, int *arr, int *expected_arr) {
qsort(arr, nel, sizeof(int), &int_compar);
return memcmp(arr, expected_arr, nel * sizeof(int));
}
int test_qsort() {
// empty
int res = sort_and_check(0, (int[]){}, (int[]){});
if (res != 0)
return -1;
// one element
res = sort_and_check(1, (int[]){42}, (int[]){42});
if (res != 0)
return -1;
// even size
res = sort_and_check(4, (int[]){4, 3, 2, 1}, (int[]){1, 2, 3, 4});
if (res != 0)
return -1;
// odd size
res =
sort_and_check(5, (int[]){1, -1, 2, 1024, 4}, (int[]){-1, 1, 2, 4, 1024});
if (res != 0)
return -1;
return 0;
}
char *str_format(const char *format, ...) {
char *str = malloc(256);
if (str == NULL) {
exit(EXIT_FAILURE);
}
va_list args;
va_start(args, format);
vsnprintf(str, 256, format, args);
va_end(args);
return str;
}
int test_vsnprintf() {
int res = 0;
char *str;
// Test %s
str = str_format("%s", "test");
res += !!strcmp(str, "test");
free(str);
// Test %s NULL
str = str_format("%s", NULL);
res += !!strcmp(str, "(null)");
free(str);
// Test % without a specifier
str = str_format("abc%");
res += !!strcmp(str, "abc");
free(str);
// Test %x
str = str_format("%x", 2042);
res += !!strcmp(str, "7fa");
free(str);
str = str_format("0x%08x", 184638698);
res += !!strcmp(str, "0x0b015cea");
free(str);
// Test %d
str = str_format("%d|%8d|%08d|%.d|%8.d|%.3d|%8.3d|%08.3d|%*d|%0*d", 5, 5, 5,
5, 5, 5, 5, 5, 8, 5, 8, 5);
res += !!strcmp(
str,
"5| 5|00000005|5| 5|005| 005| 005| 5|00000005");
free(str);
// Test %d with alternative form
str = str_format("%#.2d", 5);
res += !!strcmp(str, "05");
free(str);
// Test %f
str = str_format("%f|%8f|%08f|%.f|%8.f|%.3f|%8.3f|%08.3f|%*f|%0*f", 10.12345,
10.12345, 10.12345, 10.12345, 10.12345, 10.12345, 10.12345,
10.12345, 8, 10.12345, 8, 10.12345);
res += !!strcmp(str, "10.123450|10.123450|10.123450|10| 10|10.123| "
"10.123|0010.123|10.123450|10.123450");
free(str);
str = str_format("%f|%8f|%08f|%.f|%8.f|%.3f|%8.3f|%08.3f|%*f|%0*f", -10.12345,
-10.12345, -10.12345, -10.12345, -10.12345, -10.12345,
-10.12345, -10.12345, 8, -10.12345, 8, -10.12345);
res += !!strcmp(str, "-10.123450|-10.123450|-10.123450|-10| -10|-10.123| "
"-10.123|-010.123|-10.123450|-10.123450");
free(str);
// Test %e
str = str_format("%e|%8e|%08e|%.e|%8.e|%.3e|%8.3e|%08.3e|%*e|%0*e", 10.12345,
10.12345, 10.12345, 10.12345, 10.12345, 10.12345, 10.12345,
10.12345, 8, 10.12345, 8, 10.12345);
res += !!strcmp(
str, "1.012345e+01|1.012345e+01|1.012345e+01|1e+01| "
"1e+01|1.012e+01|1.012e+01|1.012e+01|1.012345e+01|1.012345e+01");
free(str);
str = str_format("%e|%8e|%08e|%.e|%8.e|%.3e|%8.3e|%08.3e|%*e|%0*e", -10.12345,
-10.12345, -10.12345, -10.12345, -10.12345, -10.12345,
-10.12345, -10.12345, 8, -10.12345, 8, -10.12345);
res += !!strcmp(
str,
"-1.012345e+01|-1.012345e+01|-1.012345e+01|-1e+01| "
"-1e+01|-1.012e+01|-1.012e+01|-1.012e+01|-1.012345e+01|-1.012345e+01");
free(str);
// Test %g
str = str_format("%g|%8g|%08g|%.g|%8.g|%.3g|%8.3g|%08.3g|%*g|%0*g", 10.12345,
10.12345, 10.12345, 10.12345, 10.12345, 10.12345, 10.12345,
10.12345, 8, 10.12345, 8, 10.12345);
res += !!strcmp(str, "10.1235| 10.1235|010.1235|1e+01| 1e+01|10.1| "
"10.1|000010.1| 10.1235|010.1235");
free(str);
str = str_format("%g|%8g|%08g|%.g|%8.g|%.3g|%8.3g|%08.3g|%*g|%0*g", -10.12345,
-10.12345, -10.12345, -10.12345, -10.12345, -10.12345,
-10.12345, -10.12345, 8, -10.12345, 8, -10.12345);
res += !!strcmp(str, "-10.1235|-10.1235|-10.1235|-1e+01| -1e+01|-10.1| "
"-10.1|-00010.1|-10.1235|-10.1235");
free(str);
str = str_format("%f|%8f|%08f|%.f|%8.f|%.3f|%8.3f|%08.3f|%*f|%0*f", -10.12345,
-10.12345, -10.12345, -10.12345, -10.12345, -10.12345,
-10.12345, -10.12345, 8, -10.12345, 8, -10.12345);
res += !!strcmp(str, "-10.123450|-10.123450|-10.123450|-10| -10|-10.123| "
"-10.123|-010.123|-10.123450|-10.123450");
free(str);
// Test %e
str = str_format("%e|%8e|%08e|%.e|%8.e|%.3e|%8.3e|%08.3e|%*e|%0*e", 10.12345,
10.12345, 10.12345, 10.12345, 10.12345, 10.12345, 10.12345,
10.12345, 8, 10.12345, 8, 10.12345);
res += !!strcmp(
str, "1.012345e+01|1.012345e+01|1.012345e+01|1e+01| "
"1e+01|1.012e+01|1.012e+01|1.012e+01|1.012345e+01|1.012345e+01");
free(str);
str = str_format("%e|%8e|%08e|%.e|%8.e|%.3e|%8.3e|%08.3e|%*e|%0*e", -10.12345,
-10.12345, -10.12345, -10.12345, -10.12345, -10.12345,
-10.12345, -10.12345, 8, -10.12345, 8, -10.12345);
res += !!strcmp(
str,
"-1.012345e+01|-1.012345e+01|-1.012345e+01|-1e+01| "
"-1e+01|-1.012e+01|-1.012e+01|-1.012e+01|-1.012345e+01|-1.012345e+01");
free(str);
str = str_format("%e|%8e|%08e|%.e|%8.e|%.3e|%8.3e|%08.3e|%*e|%0*e", 0.0, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 16, 0.0, 16, 0.0);
res += !!strcmp(
str,
"0.000000e+00|0.000000e+00|0.000000e+00|0e+00| "
"0e+00|0.000e+00|0.000e+00|0.000e+00| 0.000000e+00|00000.000000e+00");
free(str);
// Test %g
str = str_format("%g|%8g|%08g|%.g|%8.g|%.3g|%8.3g|%08.3g|%*g|%0*g", 10.12345,
10.12345, 10.12345, 10.12345, 10.12345, 10.12345, 10.12345,
10.12345, 8, 10.12345, 8, 10.12345);
res += !!strcmp(str, "10.1235| 10.1235|010.1235|1e+01| 1e+01|10.1| "
"10.1|000010.1| 10.1235|010.1235");
free(str);
str = str_format("%g|%8g|%08g|%.g|%8.g|%.3g|%8.3g|%08.3g|%*g|%0*g", -10.12345,
-10.12345, -10.12345, -10.12345, -10.12345, -10.12345,
-10.12345, -10.12345, 8, -10.12345, 8, -10.12345);
res += !!strcmp(str, "-10.1235|-10.1235|-10.1235|-1e+01| -1e+01|-10.1| "
"-10.1|-00010.1|-10.1235|-10.1235");
free(str);
str = str_format("%g|%8g|%08g|%.g|%8.g|%.3g|%8.3g|%08.3g|%*g|%0*g", 0.0, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 16, 0.0, 16, 0.0);
res += !!strcmp(
str, "0| 0|00000000|0| 0|0| 0|00000000| "
"0|0000000000000000");
free(str);
// Test %g with trailing zeros
str = str_format("%.14g", 1.0);
res += !!strcmp(str, "1");
free(str);
// Test %g with big number
str = str_format("%.14g", 10000000000.0);
res += !!strcmp(str, "10000000000");
free(str);
// Test %g with a precision argument
str = str_format("%.*g", 4, 10.234);
res += !!strcmp(str, "10.23");
free(str);
// Test length modifiers
str = str_format("%d %ld %lld %qd %u %lu %llu %qu", 10, 100, 4294967296,
4294967296, 10, 100, 4294967296, 4294967296);
res += !!strcmp(str,
"10 100 4294967296 4294967296 10 100 4294967296 4294967296");
free(str);
// Test %.50s with a long string
str = str_format("%.50s",
"ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ");
res += !!strcmp(str, "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWX");
free(str);
// Test precision for %x
str = str_format("%.8x-%.8x-%.2x", 10, 9999999, 9999999);
res += !!strcmp(str, "0000000a-0098967f-98967f");
free(str);
// Test unknown specifier skip
str = str_format("%I");
res += !!strcmp(str, "I");
free(str);
// Test %s with padding
const char *s = "Hello";
str = str_format("[%10s]", s);
res += !!strcmp(str, "[ Hello]");
free(str);
str = str_format("[%-10s]", s);
res += !!strcmp(str, "[Hello ]");
free(str);
str = str_format("[%*s]", 10, s);
res += !!strcmp(str, "[ Hello]");
free(str);
str = str_format("[%-*s]", 10, s);
res += !!strcmp(str, "[Hello ]");
free(str);
// Test %p with padding
str = str_format("%90p", &str);
res += (strlen(str) == 90) ? 0 : 1;
free(str);
// Test sign prepend
str = str_format("%+08d", 31501);
res += !!strcmp(str, "+0031501");
free(str);
str = str_format("%+08d", -31501);
res += !!strcmp(str, "-0031501");
free(str);
return res;
}
int test_sscanf() {
int a, b;
short c, d;
float f;
double lf;
char str[256], str1[4];
int matched = sscanf("1.23", "%d.%d", &a, &b);
if (!(matched == 2 && a == 1 && b == 23))
return -1;
matched = sscanf("abc111.42", "abc%d.%d", &a, &b);
if (!(matched == 2 && a == 111 && b == 42))
return -2;
matched = sscanf("abc", "%d.%d", &a, &b);
if (matched != 0)
return -3;
matched = sscanf("abc,8", "%[^,],%d", str, &b);
if (!(matched == 2 && strcmp(str, "abc") == 0 && b == 8))
return -4;
matched = sscanf("9,10", "%hi,%i", &c, &a);
if (!(matched == 2 && c == 9 && a == 10))
return -5;
matched = sscanf("DUMMY", "%d", &a);
if (matched != 0)
return -6;
matched = sscanf("+10 -10", "%d %d", &a, &b);
if (!(matched == 2 && a == 10 && b == -10))
return -7;
matched = sscanf("+10 -10", "%hd %hd", &c, &d);
if (!(matched == 2 && c == 10 && d == -10))
return -9;
matched = sscanf("3000\\t4", "%d %d", &a, &b);
if (!(matched == 1 && a == 3000))
return -10;
matched = sscanf("0xFF0000", "%08x", &a);
if (!(matched == 1 && a == 16711680))
return -11;
matched = sscanf("ABC\t1\t", "%s %f", str, &f);
if (!(matched == 2 && strcmp(str, "ABC") == 0 && f == 1.0))
return -12;
matched = sscanf("ABC 1\t", "%s\t%f", str, &f);
if (!(matched == 2 && strcmp(str, "ABC") == 0 && f == 1.0))
return -13;
matched = sscanf("MAX\t\t\t48.0\r\n", "%s %f", str, &f);
if (!(matched == 2 && strcmp(str, "MAX") == 0 && f == 48.0))
return -14;
matched = sscanf("011", "%i", &a);
if (!(matched == 1 && a == 9))
return -15;
matched = sscanf("09", "%i", &a);
if (!(matched == 1 && a == 0))
return -16;
matched = sscanf("FF00", "%2x%2x", &a, &b);
if (!(matched == 2 && a == 255 && b == 0))
return -17;
matched = sscanf("aa", "%10x", &a);
if (!(matched == 1 && a == 170))
return -18;
matched = sscanf("3.14159265359", "%lf", &lf);
if (!(matched == 1 && lf == 3.14159265359))
return -19;
matched = sscanf("hello123", "%[a-z]", str);
if (!(matched == 1 && strcmp(str, "hello") == 0))
return -20;
matched = sscanf("abc123", "%[^0-9]", str);
if (!(matched == 1 && strcmp(str, "abc") == 0))
return -21;
matched = sscanf("-123", "%[-0-9]", str);
if (!(matched == 1 && strcmp(str, "-123") == 0))
return -22;
matched = sscanf("a-b", "%[a-z-]", str);
if (!(matched == 1 && strcmp(str, "a-b") == 0))
return -23;
matched = sscanf("123", "%[^0-9]", str);
if (matched != 0)
return -24;
matched = sscanf("Var_123 =", "%[A-Za-z0-9_]", str);
if (!(matched == 1 && strcmp(str, "Var_123") == 0))
return -25;
matched = sscanf("NAME", "%s", str);
if (!(matched == 1 && strcmp(str, "NAME") == 0))
return -26;
matched = sscanf(" NAME", "%s", str);
if (!(matched == 1 && strcmp(str, "NAME") == 0))
return -27;
matched = sscanf("A B", "%s %s", str, str1);
if (!(matched == 2 && strcmp(str, "A") == 0 && strcmp(str1, "B") == 0))
return -28;
matched = sscanf("numJoints 110\n", " numJoints %d", &a);
if (!(matched == 1 && a == 110))
return -29;
float f1, f2, f3, f4, f5, f6;
matched = sscanf(
" \"origin\" -1 ( 0 0 0 ) ( -0.7071067095 0 0 ) // ",
"%s %d ( %f %f %f ) ( %f %f %f )", str, &a, &f1, &f2, &f3, &f4, &f5, &f6);
if (!(matched == 8 && strcmp(str, "\"origin\"") == 0 && a == -1 && f1 == 0 &&
fabs(f4 + 0.7071067095) < 1e-10 && f6 == 0))
return -30;
return 0;
}
int test_swscanf() {
int a, b;
int matched = swscanf(L"1.23", L"%d.%d", &a, &b);
if (!(matched == 2 && a == 1 && b == 23))
return -1;
matched = swscanf(L"str_01", L"str_%2d", &a);
if (!(matched == 1 && a == 1))
return -2;
return 0;
}
int test_realloc() {
void *ptr = realloc(NULL, 32);
memmove(ptr, "abcd", 4);
ptr = realloc(ptr, 64);
int res = memcmp(ptr, "abcd", 4);
free(ptr);
return res == 0 ? 0 : -1;
}
int test_atof() {
if (atof("1") != 1)
return -1;
if (atof("-1") != -1)
return -2;
if (atof("01") != 1)
return -3;
if (atof("-01") != -1)
return -4;
if (atof("10") != 10)
return -5;
if (atof("-10") != -10)
return -6;
if (atof("010") != 10)
return -7;
if (atof("-010") != -10)
return -8;
if (atof("1.0") != 1)
return -9;
if (atof("-1.0") != -1)
return -10;
if (atof("01.0") != 1)
return -11;
if (atof("-01.0") != -1)
return -12;
if (atof("10.0") != 10)
return -13;
if (atof("-10.0") != -10)
return -14;
if (atof("010.0") != 10)
return -15;
if (atof("-010.0") != -10)
return -16;
if (atof("1.5") != 1.5)
return -17;
if (atof("-1.5") != -1.5)
return -18;
if (atof("01.5") != 1.5)
return -19;
if (atof("-01.5") != -1.5)
return -20;
if (atof("10.5") != 10.5)
return -21;
if (atof("-10.5") != -10.5)
return -22;
if (atof("010.5") != 10.5)
return -23;
if (atof("-010.5") != -10.5)
return -24;
if (atof(" +123.456e7with text right after") != 1234560000)
return -25;
if (atof("Text before a number 123.456") != 0)
return -26;
return 0;
}
int test_strtof() {
char *text = "1";
char *endptr;
if (strtof(text, &endptr) != 1.0 || endptr != text + 1)
return -1;
text = "-1";
if (strtof(text, &endptr) != -1.0 || endptr != text + 2)
return -2;
text = "01";
if (strtof(text, &endptr) != 1.0 || endptr != text + 2)
return -3;
text = "-01";
if (strtof(text, &endptr) != -1.0 || endptr != text + 3)
return -4;
text = "10";
if (strtof(text, &endptr) != 10.0 || endptr != text + 2)
return -5;
text = "-10";
if (strtof(text, &endptr) != -10.0 || endptr != text + 3)
return -6;
text = "010";
if (strtof(text, &endptr) != 10.0 || endptr != text + 3)
return -7;
text = "-010";
if (strtof(text, &endptr) != -10.0 || endptr != text + 4)
return -8;
text = "1.0";
if (strtof(text, &endptr) != 1.0 || endptr != text + 3)
return -9;
text = "-1.0";
if (strtof(text, &endptr) != -1.0 || endptr != text + 4)
return -10;
text = "01.0";
if (strtof(text, &endptr) != 1.0 || endptr != text + 4)
return -11;
text = "-01.0";
if (strtof(text, &endptr) != -1.0 || endptr != text + 5)
return -12;
text = "10.0";
if (strtof(text, &endptr) != 10.0 || endptr != text + 4)
return -13;
text = "-10.0";
if (strtof(text, &endptr) != -10.0 || endptr != text + 5)
return -14;
text = "010.0";
if (strtof(text, &endptr) != 10.0 || endptr != text + 5)
return -15;
text = "-010.0";
if (strtof(text, &endptr) != -10.0 || endptr != text + 6)
return -16;
text = "1.5";
if (strtof(text, &endptr) != 1.5 || endptr != text + 3)
return -17;
text = "-1.5";
if (strtof(text, &endptr) != -1.5 || endptr != text + 4)
return -18;
text = "01.5";
if (strtof(text, &endptr) != 1.5 || endptr != text + 4)
return -19;
text = "-01.5";
if (strtof(text, &endptr) != -1.5 || endptr != text + 5)
return -20;
text = "10.5";
if (strtof(text, &endptr) != 10.5 || endptr != text + 4)
return -21;
text = "-10.5";
if (strtof(text, &endptr) != -10.5 || endptr != text + 5)
return -22;
text = "010.5";
if (strtof(text, &endptr) != 10.5 || endptr != text + 5)
return -23;
text = "-010.5";
if (strtof(text, &endptr) != -10.5 || endptr != text + 6)
return -24;
text = " +123.456e7with text right after";
if (strtof(text, &endptr) != 1234560000.0 || endptr != text + 12)
return -25;
text = "Text before a number 123.456";
if (strtof(text, &endptr) != 0.0 || endptr != text + 0)
return -26;
text = "1.5";
if (strtof(text, NULL) != 1.5)
return -27;
return 0;
}
int test_strtoul() {
char *text = "0xcccccccc";
char *endptr;
if (strtoul(text, &endptr, 16) != 3435973836 || endptr != text + 10) {
return -1;
}
text = "12345";
if (strtoul(text, &endptr, 10) != 12345UL || endptr != text + 5) {
return -2;
}
text = "123abc";
if (strtoul(text, &endptr, 10) != 123UL || endptr != text + 3) {
return -3;
}
text = "abc";
if (strtoul(text, &endptr, 10) != 0UL || endptr != text) {
return -4;
}
text = "-1";
if (strtoul(text, &endptr, 10) != (unsigned long)-1 || endptr != text + 2) {
return -5;
}
text = "Ff";
if (strtoul(text, &endptr, 16) != 255UL || endptr != text + 2) {
return -6;
}
text = " +42abc";
if (strtoul(text, &endptr, 10) != 42UL || endptr != text + 6) {
return -7;
}
#ifndef DEFINE_ME_WHEN_BUILDING_ON_MACOS
// Test for overflow. "4294967296" is ULONG_MAX + 1 on a 32-bit system.
text = "4294967296";
if (strtoul(text, &endptr, 10) != 4294967295 || endptr != text + 10) {
return -8;
}
#endif
text = "4294967295";
if (strtoul(text, &endptr, 10) != 4294967295 || endptr != text + 10) {
return -9;
}
text = "15";
if (strtoul(text, &endptr, 0) != 15UL || endptr != text + 2) {
return -10;
}
text = "017"; // octal: 1*8 + 7 = 15
if (strtoul(text, &endptr, 0) != 15UL || endptr != text + 3) {
return -11;
}
text = "0x0F";
if (strtoul(text, &endptr, 0) != 15UL || endptr != text + 4) {
return -12;
}
text = "";
if (strtoul(text, &endptr, 10) != 0UL || endptr != text) {
return -13;
}
text = " ";
if (strtoul(text, &endptr, 10) != 0UL || endptr != text) {
return -14;
}
text = "1101"; // binary: 8 + 4 + 1 = 13
if (strtoul(text, &endptr, 2) != 13UL || endptr != text + 4) {
return -15;
}
text = "zZ"; // base 36: 35*36 + 35 = 1295
if (strtoul(text, &endptr, 36) != 1295UL || endptr != text + 2) {
return -16;
}
text = "77"; // octal: 7*8 + 7 = 63
if (strtoul(text, &endptr, 8) != 63UL || endptr != text + 2) {
return -17;
}
return 0;
}
#ifdef DEFINE_ME_WHEN_BUILDING_ON_MACOS
#define MAX_LONG 9223372036854775807
#else
#define MAX_LONG 2147483647
#endif
int test_strtol() {
const char *p = "10 200000000000000000000000000000 30 -40 junk";
long res[] = {10, MAX_LONG, 30, -40, 0};
int count = sizeof(res) / sizeof(long);
for (int i = 0; i < count; i++) {
char *endp = NULL;
long l = strtol(p, &endp, 10);
if (p == endp)
break;
p = endp;
if (res[i] != l) {
return -(i + 1);
}
}
p = "-";
long l = strtol(p, NULL, 0);
if (l != 0) {
return -count;
}
p = "+";
l = strtol(p, NULL, 0);
if (l != 0) {
return -(count + 1);
}
p = "+-+";
l = strtol(p, NULL, 0);
if (l != 0) {
return -(count + 2);
}
p = "0x123 +0x123 -0x123";
long res2[] = {291, 291, -291};
int count2 = sizeof(res2) / sizeof(long);
for (int i = 0; i < count2; i++) {
char *endp = NULL;
l = strtol(p, &endp, 16);
if (p == endp)
break;
p = endp;
if (res2[i] != l) {
return -(count + 2 + i + 1);
}
}
return 0;
}
int test_getcwd_chdir() {
char buf[256];
char *buf2 = getcwd(buf, sizeof buf);
if (!buf2 || buf2 != buf || strcmp("/", buf))
return -1;
if (!chdir("does_not_exist") || !chdir("/does/not/exist"))
return -1;
if (chdir("/var/"))
return -1;
if (chdir("mobile/Applications"))
return -1;
char *buf3 = getcwd(NULL, 0);
if (!buf3 || strcmp("/var/mobile/Applications", buf3))
return -1;
free(buf3);
char *buf5 = getcwd(buf, 4); // too small
if (buf5)
return -1;
if (chdir(".."))
return -1;
char *buf6 = getcwd(buf, sizeof buf);
if (!buf6 || buf6 != buf || strcmp("/var/mobile", buf6))
return -1;
FILE *fake_file = fopen("TestApp", "r"); // doesn't exist in this directory
if (fake_file) {
fclose(fake_file);
return -1;
}
if (chdir("Applications/00000000-0000-0000-0000-000000000000/TestApp.app"))
return -1;
if (!chdir("TestApp")) // isn't a directory
return -1;
FILE *real_file = fopen("TestApp", "r");
if (!real_file)
return -1;
fclose(real_file);
if (chdir("/"))
return -1;
return 0;
}
sem_t *semaphore;
int shared_int = 0;
void sem_thread_func() {
while (1) {
if (sem_trywait(semaphore) == -1) {
return;
}
shared_int = -1;
sem_post(semaphore);
usleep(100);
}
}
int test_sem() {
semaphore = sem_open("sem_test", O_CREAT, 0644, 1);
if (semaphore == SEM_FAILED) {
printf("Error opening semaphore\n");
return -1;
}
pthread_t *my_thread = (pthread_t *)malloc(sizeof(pthread_t));
pthread_create(my_thread, NULL, (void *)sem_thread_func, NULL);
usleep(200);
sem_wait(semaphore);
shared_int = 1;
usleep(200);
sem_close(semaphore);
sem_unlink("sem_test");
if (shared_int != 1) {
return -1;
}
// Check that reopen is fine
semaphore = sem_open("sem_test", O_CREAT, 0644, 1);
if (semaphore == SEM_FAILED) {
printf("Error opening semaphore\n");
return -1;
}
// Sem @ 0
if (sem_trywait(semaphore) == -1) {
return -1;
}
// Sem still @ 0, should not lock
if (sem_trywait(semaphore) == 0) {
return -1;
}
// Sem @ 1, should be able to relock
sem_post(semaphore);
if (sem_trywait(semaphore) == -1) {
return -1;
}
sem_close(semaphore);
sem_unlink("sem_test");
return 0;
}
sem_t *mt_semaphore;
void mtsem_thread() {
sem_wait(mt_semaphore);
sem_post(mt_semaphore);
}
int test_mtsem() {
mt_semaphore = sem_open("mtsem_test", O_CREAT, 0644, 0);
if (mt_semaphore == SEM_FAILED) {
printf("Error opening semaphore\n");
return -1;
}
pthread_t *my_thread = (pthread_t *)malloc(sizeof(pthread_t));
pthread_create(my_thread, NULL, (void *)mtsem_thread, NULL);
pthread_t *my_thread2 = (pthread_t *)malloc(sizeof(pthread_t));
pthread_create(my_thread2, NULL, (void *)mtsem_thread, NULL);
usleep(1);
usleep(1);
sem_post(mt_semaphore);
pthread_join(*my_thread, NULL);
pthread_join(*my_thread2, NULL);
return 0;
}
int done = 0, done2 = 0;
pthread_mutex_t m;
pthread_cond_t c, c2;
void thr_exit() {
pthread_mutex_lock(&m);
done = 1;
pthread_cond_signal(&c);
pthread_mutex_unlock(&m);
}
void *child(void *arg) {
thr_exit();
return NULL;
}
void *child2(void *arg) {
pthread_mutex_lock(&m);
while (done == 0) {
pthread_cond_wait(&c2, &m);
}
pthread_mutex_unlock(&m);
return NULL;
}
void thr_join() {
pthread_mutex_lock(&m);
while (done == 0) {
pthread_cond_wait(&c, &m);
}
pthread_mutex_unlock(&m);
}
int test_cond_var() {
pthread_t p;
pthread_mutex_init(&m, NULL);
pthread_cond_init(&c, NULL);
pthread_create(&p, NULL, child, NULL);
thr_join();
// Should wake up all threads
pthread_t p1, p2, p3;
pthread_cond_init(&c2, NULL);
pthread_create(&p1, NULL, child, NULL);
pthread_create(&p2, NULL, child, NULL);
pthread_create(&p3, NULL, child, NULL);
usleep(100);
pthread_mutex_lock(&m);
done = 1;
pthread_cond_broadcast(&c);
pthread_mutex_unlock(&m);
pthread_join(p1, NULL);
pthread_join(p2, NULL);
pthread_join(p3, NULL);
return done == 1 ? 0 : -1;
}
int test_strncpy() {
char *src = "test\0abcd";
char dst[10];
char *retval;
char expected1[] = "test\x00\x7F\x7F\x7F\x7F\x7F";
memset(dst, 0x7F, 10);
retval = strncpy(dst, src, 5);
if (retval != dst || memcmp(retval, expected1, 10))
return 1;
char expected2[] = "te\x7F\x7F\x7F\x7F\x7F\x7F\x7F\x7F";
memset(dst, 0x7F, 10);
retval = strncpy(dst, src, 2);
if (retval != dst || memcmp(retval, expected2, 10))
return 2;
char expected3[] = "test\x00\x00\x00\x00\x00\x00";
memset(dst, 0x7F, 10);
retval = strncpy(dst, src, 10);
if (retval != dst || memcmp(retval, expected3, 10))
return 3;
return 0;
}
int test_strncat() {
{
char uno[] = "uno\0zzzz";
char dos[] = "dos\0ZZZZ";
char expected[] = "unodos\0z";
char *new = strncat(uno, dos, 100);
if (new != uno || memcmp(new, expected, 8))
return 1;
}
{
char uno[] = "uno\0zzzz";
char dos[] = "dos\0ZZZZ";
char expected[] = "unod\0zzz";
char *new = strncat(uno, dos, 1);
if (new != uno || memcmp(new, expected, 8))
return 2;
}
{
char uno[] = "uno\0zzzz";
char dos[] = "dosZZZZZ";
char expected[] = "unodos\0z";
char *new = strncat(uno, dos, 3);
if (new != uno || memcmp(new, expected, 8))
return 3;
}
return 0;
}
int test_strlcpy() {
{
char src[7] = "origen";
char dst[15] = "destinodestino";
char expected[] = "or\0tinodestino";
int ret = strlcpy(dst, src, 3);
if (ret != 6 || memcmp(dst, expected, 15)) {
printf("%d %s\t", ret, dst);
return 1;
}
}
{
char src[7] = "origen";
char dst[15] = "destinodestino";
char expected[] = "orige\0odestino";
int ret = strlcpy(dst, src, 6);
if (ret != 6 || memcmp(dst, expected, 15)) {
printf("%d %s\t", ret, dst);
return 2;
}
}
{
char src[7] = "origen";
char dst[15] = "destinodestino";
char expected[] = "origen\0destino";
int ret = strlcpy(dst, src, 9);
if (ret != 6 || memcmp(dst, expected, 15)) {
printf("%d %s\t", ret, dst);
return 3;
}
}
return 0;
}
int test_setlocale() {
char *locale;
// Test getting default locale
locale = setlocale(LC_ALL, NULL);
if (strcmp(locale, "C") != 0) {
return 1;
}
// Test setting a locale category
locale = setlocale(LC_NUMERIC, "POSIX");
if (strcmp(locale, "POSIX") != 0) {
return 2;
}
// Test if other categories are unaffected
locale = setlocale(LC_TIME, NULL);
if (strcmp(locale, "C") != 0) {
return 3;
}
// Set C locale back for numeric
locale = setlocale(LC_NUMERIC, "C");
if (strcmp(locale, "C") != 0) {
return 4;
}
return 0;
}
const int PATH_BUF_SIZE = 256;
// static array for path: not great, not terrible
char path[PATH_BUF_SIZE];
const char *path_test_app() {
#ifdef DEFINE_ME_WHEN_BUILDING_ON_MACOS
// assume project dir as cwd
return "./tests/TestApp.app";
#else
bzero(path, PATH_BUF_SIZE);
CFBundleRef mainBundle = CFBundleGetMainBundle();
CFURLRef bundleURL = CFBundleCopyBundleURL(mainBundle);
CFURLGetFileSystemRepresentation(bundleURL,
true, // Resolve against base (absolute path)
(UInt8 *)path, // Output buffer
PATH_BUF_SIZE // Buffer size
);
CFRelease(bundleURL);
return path;
#endif
}
int test_dirent() {
struct dirent *dp;
DIR *dirp = opendir(path_test_app());
if (dirp == NULL) {
return -1;
}
char *contents[] = {"TestApp", "Info.plist", "PkgInfo"};
int counts[] = {1, 1, 1};
int total = sizeof(contents) / sizeof(char *);
while ((dp = readdir(dirp)) != NULL) {
for (int i = 0; i < total; i++) {
if (strcmp(contents[i], dp->d_name) == 0) {
counts[i]--;
break;
}
}
}
closedir(dirp);
for (int i = 0; i < total; i++) {
if (counts[i] != 0) {
return -2;
}
}
return 0;
}
int test_scandir() {
struct dirent **namelist;
int n = scandir(path_test_app(), &namelist, NULL, NULL);
if (n < 0) {
return -1;
}
char *contents[] = {"TestApp", "Info.plist", "PkgInfo"};
int counts[] = {1, 1, 1};
int total = sizeof(contents) / sizeof(char *);
while (n--) {
for (int i = 0; i < total; i++) {
if (strcmp(contents[i], namelist[n]->d_name) == 0) {
counts[i]--;
break;
}
}
free(namelist[n]);
}
free(namelist);
for (int i = 0; i < total; i++) {
if (counts[i] != 0) {
return -2;
}
}
return 0;
}
int test_read_directory_as_fd() {
FILE *dir_stream = fopen(path_test_app(), "r");
if (dir_stream == NULL) {
return -1;
}
char buffer[1024];
size_t bytes_read = fread(buffer, 1, 4, dir_stream);
if (bytes_read != 0) {
return -2;
}
if (errno != EISDIR) {
return -3;
}
fclose(dir_stream);
return 0;
}
int test_strchr() {
char *src = "abc";
if (strchr(src, 'a')[0] != 'a' || strrchr(src, 'a')[0] != 'a')
return -1;
if (strchr(src, 'b')[0] != 'b' || strrchr(src, 'b')[0] != 'b')
return -2;
if (strchr(src, 'c')[0] != 'c' || strrchr(src, 'c')[0] != 'c')
return -3;
if (strchr(src, '\0')[0] != '\0' || strrchr(src, '\0')[0] != '\0')
return -4;
if (strchr(src, 'd') != NULL || strrchr(src, 'd') != NULL)
return -5;
return 0;
}
int test_swprintf() {
wchar_t wcsbuf[20];
int res = swprintf(wcsbuf, 20, L"%s", "abc");
if (res != 3)
return -1;
res = swprintf(wcsbuf, 2, L"%d", 510);
if (res != -1)
return -2;
res = swprintf(wcsbuf, 20, L"%S", L"abc");
if (res != 3)
return -3;
return 0;
}
int test_realpath() {
char buf[256];
if (chdir(path_test_app()))
return -1;
// absolute path
char *res = realpath("/usr", buf);
if (!res || strcmp(res, "/usr") != 0)
return -2;
// relative path
res = realpath("TestApp", buf);
char *cwd = getcwd(NULL, 0);
if (!res || strncmp(cwd, res, strlen(cwd)) != 0 ||
strncmp("/TestApp", res + strlen(cwd), 8) != 0)
return -3;
// `..` and `.` resolution
res = realpath("../TestApp.app/./TestApp", buf);
if (!res || strncmp(cwd, res, strlen(cwd)) != 0 ||
strncmp("/TestApp", res + strlen(cwd), 8) != 0)
return -4;
return 0;
}
int test_ungetc() {
FILE *file = fopen("test_ungetc", "r");
if (file == NULL) {
return -1;
}
char c = getc(file);
if (c != 'a') {
fclose(file);
return -2;
}
// ungetc with _wrong_ char
c = ungetc('b', file);
if (c != 'b') {
fclose(file);
return -3;
}
char buf[4];
memset(buf, '\0', 4);
size_t read = fread(buf, 1, 3, file);
fclose(file);
if (read != 3) {
return -4;
}
if (strcmp(buf, "baa") != 0) {
return -5;
}
return 0;
}
int test_fscanf() {
char str[256];
int a;
float f;
FILE *file = fopen("test_fscanf", "r");
if (file == NULL) {
return -1;
}
int matched = fscanf(file, "%s", str);
if (!(matched == 1 && strcmp(str, "no_spaces_line") == 0)) {
return -2;
}
matched = fscanf(file, "%s %d", str, &a);
if (!(matched == 2 && strcmp(str, "one") == 0 && a == -100)) {
return -3;
}
matched = fscanf(file, "%s", str);
if (!(matched == 1 && strcmp(str, "string") == 0)) {
return -4;
}
matched = fscanf(file, "%f", &f);
if (!(matched == 1 && fabs(f - 3.14) < 0.001)) {
return -5;
}
matched = fscanf(file, "%s", str);
if (matched != -1) { // EOF
return -6;
}
fclose(file);
return 0;
}
// Below tests are on par with test_sscanf(),
// but reading data from a file instead.
// Please update those as well if you add new
// test cases to test_sscanf()
int test_fscanf_new() {
FILE *file = fopen("test_fscanf_new", "r");
if (!file)
return -1;
#define SKIP_LINE(f) \
do { \
int ch; \
while ((ch = fgetc(f)) != '\n' && ch != -1) \
; \
} while (0)
int a, b, matched;
short c, d;
float f, f1, f2, f3, f4, f5, f6;
double lf;
char str[256], str1[4];
matched = fscanf(file, "%d.%d", &a, &b);
if (!(matched == 2 && a == 1 && b == 23))
return -2;
SKIP_LINE(file);
matched = fscanf(file, "abc%d.%d", &a, &b);
if (!(matched == 2 && a == 111 && b == 42))
return -3;
SKIP_LINE(file);
matched = fscanf(file, "%d.%d", &a, &b);
if (matched != 0)
return -4;
SKIP_LINE(file);
matched = fscanf(file, "%[^,],%d", str, &b);
if (!(matched == 2 && strcmp(str, "abc") == 0 && b == 8))
return -5;
SKIP_LINE(file);
matched = fscanf(file, "%hi,%i", &c, &a);
if (!(matched == 2 && c == 9 && a == 10))
return -6;
SKIP_LINE(file);
matched = fscanf(file, "%d", &a);
if (matched != 0)
return -7;
SKIP_LINE(file);
matched = fscanf(file, "%d %d", &a, &b);
if (!(matched == 2 && a == 10 && b == -10))
return -8;
SKIP_LINE(file);
matched = fscanf(file, "%hd %hd", &c, &d);
if (!(matched == 2 && c == 10 && d == -10))
return -9;
SKIP_LINE(file);
matched = fscanf(file, "%d %d", &a, &b);
if (!(matched == 1 && a == 3000))
return -10;
SKIP_LINE(file);
matched = fscanf(file, "%08x", &a);
if (!(matched == 1 && a == 16711680))
return -11;
SKIP_LINE(file);
matched = fscanf(file, "%s %f", str, &f);
if (!(matched == 2 && strcmp(str, "ABC") == 0 && f == 1.0f))
return -12;
SKIP_LINE(file);
matched = fscanf(file, "%s\t%f", str, &f);
if (!(matched == 2 && strcmp(str, "ABC") == 0 && f == 1.0f))
return -13;
SKIP_LINE(file);
matched = fscanf(file, "%s %f", str, &f);
if (!(matched == 2 && strcmp(str, "MAX") == 0 && f == 48.0f))
return -14;
SKIP_LINE(file);
matched = fscanf(file, "%i", &a);
if (!(matched == 1 && a == 9))
return -15;
SKIP_LINE(file);
matched = fscanf(file, "%i", &a);
if (!(matched == 1 && a == 0))
return -16;
SKIP_LINE(file);
matched = fscanf(file, "%2x%2x", &a, &b);
if (!(matched == 2 && a == 0xFF && b == 0x00))
return -17;
SKIP_LINE(file);
matched = fscanf(file, "%10x", &a);
if (!(matched == 1 && a == 0xAA))
return -18;
SKIP_LINE(file);
matched = fscanf(file, "%lf", &lf);
if (!(matched == 1 && lf == 3.14159265359))
return -19;
SKIP_LINE(file);
matched = fscanf(file, "%[a-z]", str);
if (!(matched == 1 && strcmp(str, "hello") == 0))
return -20;
SKIP_LINE(file);
matched = fscanf(file, "%[^0-9]", str);
if (!(matched == 1 && strcmp(str, "abc") == 0))
return -21;
SKIP_LINE(file);
matched = fscanf(file, "%[-0-9]", str);
if (!(matched == 1 && strcmp(str, "-123") == 0))
return -22;
SKIP_LINE(file);
matched = fscanf(file, "%[a-z-]", str);
if (!(matched == 1 && strcmp(str, "a-b") == 0))
return -23;
SKIP_LINE(file);
matched = fscanf(file, "%[^0-9]", str);
if (matched != 0)
return -24;
SKIP_LINE(file);
matched = fscanf(file, "%[A-Za-z0-9_]", str);
if (!(matched == 1 && strcmp(str, "Var_123") == 0))
return -25;
SKIP_LINE(file);
matched = fscanf(file, "%s", str);
if (!(matched == 1 && strcmp(str, "NAME") == 0))
return -26;
SKIP_LINE(file);
matched = fscanf(file, "%s", str);
if (!(matched == 1 && strcmp(str, "NAME") == 0))
return -27;
SKIP_LINE(file);
matched = fscanf(file, "%s %s", str, str1);
if (!(matched == 2 && strcmp(str, "A") == 0 && strcmp(str1, "B") == 0))
return -28;
SKIP_LINE(file);
matched = fscanf(file, " numJoints %d", &a);
if (!(matched == 1 && a == 110))
return -29;
SKIP_LINE(file);
matched = fscanf(file, " %s %d ( %f %f %f ) ( %f %f %f )", str, &a, &f1, &f2,
&f3, &f4, &f5, &f6);
if (!(matched == 8 && strcmp(str, "\"origin\"") == 0 && a == -1 &&
f1 == 0.0f && fabs(f4 + 0.7071067095f) < 1e-10f && f6 == 0.0f))
return -30;
fclose(file);
return 0;
}
int test_CGImage_JPEG() {
FILE *file = fopen("test_1x1_black_pixel.jpg", "r");
if (file == NULL) {
return -1;
}
char buf[720];
memset(buf, '\0', 720);
size_t read = fread(buf, 1, 720, file);
fclose(file);
if (read != 720) {
return -2;
}
CFDataRef dataRef = CFDataCreate(NULL, buf, sizeof(buf));
if (dataRef == NULL) {
return -3;
}
CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData(dataRef);
if (dataRef == NULL) {
return -4;
}
CGImageRef imageRef = CGImageCreateWithJPEGDataProvider(
dataProvider, NULL, 1 /* true */, 0 /* kCGRenderingIntentDefault */);
if (imageRef == NULL) {
return -5;
}
size_t width = CGImageGetWidth(imageRef);
size_t height = CGImageGetHeight(imageRef);
if (!(width == 1 && height == 1)) {
return -6;
}
CFDataRef rawData = CGDataProviderCopyData(CGImageGetDataProvider(imageRef));
const unsigned char *bytes = CFDataGetBytePtr(rawData);
// Check that pixel is indeed a RGB black one
if (!(bytes[0] == 0 && bytes[1] == 0 && bytes[2] == 0)) {
return -7;
}
CFRelease(rawData);
CFRelease(imageRef);
CFRelease(dataProvider);
return 0;
}
int test_CFStringFind() {
CFStringRef a =
CFStringCreateWithCString(NULL, "/a/b/c/b", kCFStringEncodingASCII);
CFStringRef b = CFStringCreateWithCString(NULL, "/b", kCFStringEncodingASCII);
CFStringRef d = CFStringCreateWithCString(NULL, "/d", kCFStringEncodingASCII);
// 0 for default options
CFRange r = CFStringFind(a, b, 0);
if (!(r.location == 2 && r.length == 2)) {
return -1;
}
// 4 for kCFCompareBackwards
r = CFStringFind(a, b, 4);
if (!(r.location == 6 && r.length == 2)) {
return -2;
}
// search string in itself
r = CFStringFind(a, a, 0);
if (!(r.location == 0 && r.length == 8)) {
return -3;
}
// search string in itself, backwards
r = CFStringFind(a, a, 4);
if (!(r.location == 0 && r.length == 8)) {
return -4;
}
// not found case
r = CFStringFind(a, d, 0);
if (!(r.location == -1 && r.length == 0)) {
return -5;
}
// 1 for kCFCompareCaseInsensitive
CFStringRef b2 = CFStringCreateWithCString(NULL, "/B", 0x0600);
r = CFStringFind(a, b2, 1);
if (!(r.location == 2 && r.length == 2)) {
return -6;
}
return 0;
}
int test_strcspn() {
size_t res = strcspn("abcdef", "abcd");
if (res != 0) {
return -1;
}
res = strcspn("abcdef", "ef");
if (res != 4) {
return -2;
}
res = strcspn("abcdef", "");
if (res != 6) {
return -3;
}
return 0;
}
int test_mbstowcs() {
wchar_t wbuffer[64];
char buffer[64];
size_t res;
char *test_str = "Hello, World!";
res = mbstowcs(wbuffer, test_str, 64);
if (res == (size_t)-1) {
return -1;
}
res = wcstombs(buffer, wbuffer, 64);
if (res == (size_t)-1) {
return -2;
}
if (strcmp(test_str, buffer) != 0) {
return -3;
}
return 0;
}
int test_CFMutableString() {
CFMutableStringRef mut_str = CFStringCreateMutable(NULL, 0);
CFStringRef fmt = CFStringCreateWithCString(NULL, "%d %.2f", 0x0600);
CFStringAppendFormat(mut_str, NULL, fmt, -100, 3.14);
CFStringRef res = CFStringCreateWithCString(NULL, "-100 3.14", 0x0600);
if (CFStringCompare(mut_str, res, 0) != 0) {
return -1;
}
return 0;
}
int test_fwrite() {
FILE *some_file = fopen("TestApp", "r");
size_t res = fwrite(NULL, 1, 1, some_file);
fclose(some_file);
if (res != 0) {
return -1;
}
return 0;
}
int test_open() {
int fd;
// Test opening directories
fd = open("/usr", O_RDONLY);
if (fd == -1) {
return -1;
}
close(fd);
fd = open("/usr", O_WRONLY);
if (fd != -1) {
close(fd);
return -2;
}
fd = open("/usr", O_RDWR);
if (fd != -1) {
close(fd);
return -3;
}
return 0;
}
int test_close() {
if (close(0) != 0)
return -1;
if (close(-1) == 0)
return -2;
if (close(1000) == 0)
return -3;
return 0;
}
int test_CFMutableDictionary_NullCallbacks() {
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, NULL, NULL);
if (dict == NULL) {
return -1;
}
const char *key = "Key";
const char *value = "Value";
CFDictionaryAddValue(dict, key, value);
const void *retrievedValue = CFDictionaryGetValue(dict, key);
if (retrievedValue != value) {
CFRelease(dict);
return -2;
}
const char *valueNew = "NewValue";
CFDictionaryAddValue(dict, key, valueNew);
retrievedValue = CFDictionaryGetValue(dict, key);
if (retrievedValue != value) {
CFRelease(dict);
return -3;
}
CFDictionarySetValue(dict, key, NULL);
retrievedValue = CFDictionaryGetValue(dict, key);
if (retrievedValue != NULL) {
CFRelease(dict);
return -4;
}
CFDictionarySetValue(dict, key, valueNew);
retrievedValue = CFDictionaryGetValue(dict, key);
if (retrievedValue != valueNew) {
CFRelease(dict);
return -5;
}
CFDictionaryRemoveValue(dict, key);
retrievedValue = CFDictionaryGetValue(dict, key);
if (retrievedValue != NULL) {
CFRelease(dict);
return -6;
}
CFDictionaryAddValue(dict, key, value);
retrievedValue = CFDictionaryGetValue(dict, key);
if (retrievedValue != value) {
CFRelease(dict);
return -7;
}
CFIndex count = CFDictionaryGetCount(dict);
if (count != 1) {
CFRelease(dict);
return -8;
}
const void **keys = malloc(sizeof(char *) * count);
const void **values = malloc(sizeof(char *) * count);
CFDictionaryGetKeysAndValues(dict, keys, values);
if (keys[0] != key || values[0] != value) {
free(keys);
free(values);
CFRelease(dict);
return -9;
}
free(keys);
free(values);
CFDictionaryRemoveAllValues(dict);
count = CFDictionaryGetCount(dict);
if (count != 0) {
CFRelease(dict);
return -10;
}
count = CFDictionaryGetCount(dict);
if (count != 0) {
CFRelease(dict);
return -11;
}
CFRelease(dict);
return 0;
}
// Counters for checking key/value callbacks
static int keyRetainCount = 0;
static int keyReleaseCount = 0;
static int keyEqualCount = 0;
static int keyHashCount = 0;
static int valueRetainCount = 0;
static int valueReleaseCount = 0;
static int valueEqualCount = 0;
// Custom CFDictionary key/value callbacks
const void *TestKeyRetain(CFAllocatorRef allocator, const void *value) {
keyRetainCount++;
if (value == NULL) {
return NULL;
}
return strdup((const char *)value);
}
void TestKeyRelease(CFAllocatorRef allocator, const void *value) {
keyReleaseCount++;
if (value == NULL) {
return;
}
free((void *)value);
}
Boolean TestKeyEqual(const void *value1, const void *value2) {
keyEqualCount++;
if (value1 == value2) {
return 1;
}
if (value1 == NULL || value2 == NULL) {
return 0;
}
return strcmp((const char *)value1, (const char *)value2) == 0;
}
CFHashCode TestKeyHash(const void *value) {
keyHashCount++;
return (value == NULL) ? 0 : 5;
}
const void *TestValueRetain(CFAllocatorRef allocator, const void *value) {
valueRetainCount++;
return (value == NULL) ? NULL : strdup((const char *)value);
}
void TestValueRelease(CFAllocatorRef allocator, const void *value) {
valueReleaseCount++;
if (value == NULL) {
return;
}
free((void *)value);
}
Boolean TestValueEqual(const void *value1, const void *value2) {
valueEqualCount++;
if (value1 == value2) {
return 1;
}
if (value1 == NULL || value2 == NULL) {
return 0;
}
return strcmp((const char *)value1, (const char *)value2) == 0;
}
CFDictionaryKeyCallBacks testKeyCallBacks = {0, // version
TestKeyRetain,
TestKeyRelease,
NULL,
TestKeyEqual,
TestKeyHash};
CFDictionaryValueCallBacks testValueCallBacks = {
0, // version
TestValueRetain, TestValueRelease, NULL, TestValueEqual};
int test_CFMutableDictionary_CustomCallbacks_PrimitiveTypes() {
// Reset counters
keyRetainCount = keyReleaseCount = keyEqualCount = keyHashCount = 0;
valueRetainCount = valueReleaseCount = valueEqualCount = 0;
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(
NULL, 0, &testKeyCallBacks, &testValueCallBacks);
if (dict == NULL) {
return -1;
}
CFIndex count = CFDictionaryGetCount(dict);
if (count != 0) {
CFRelease(dict);
return -2;
}
const char *key = "Key";
const char *value = "Value";
CFDictionaryAddValue(dict, key, value);
// Hash key function should be called at least once
if (keyRetainCount != 1 || keyHashCount < 1 || valueRetainCount != 1) {
CFRelease(dict);
return -3;
}
count = CFDictionaryGetCount(dict);
if (count != 1) {
CFRelease(dict);
return -4;
}
const void *retrievedValue = CFDictionaryGetValue(dict, key);
if (retrievedValue == NULL) {
CFRelease(dict);
return -5;
}
if (strcmp((const char *)retrievedValue, value) != 0) {
CFRelease(dict);
return -6;
}
if (keyEqualCount < 1) {
CFRelease(dict);
return -7;
}
const char *valueNew = "NewValue";
CFDictionaryAddValue(dict, key, valueNew);
// The key already exists, so the value should not be added
if (keyRetainCount != 1 || valueRetainCount != 1) {
CFRelease(dict);
return -8;
}
count = CFDictionaryGetCount(dict);
if (count != 1) {
CFRelease(dict);
return -9;
}
retrievedValue = CFDictionaryGetValue(dict, key);
if (strcmp((const char *)retrievedValue, value) != 0) {
CFRelease(dict);
return -10;
}
CFDictionarySetValue(dict, key, NULL);
if (valueReleaseCount != 1 || valueRetainCount != 2) {
CFRelease(dict);
return -11;
}
// Check that count is 1 after setting value to NULL
// (NULL is a valid value for CFDictionary!)
count = CFDictionaryGetCount(dict);
if (count != 1) {
CFRelease(dict);
return -12;
}
retrievedValue = CFDictionaryGetValue(dict, key);
if (retrievedValue != NULL) {
CFRelease(dict);
return -13;
}
if (keyReleaseCount != 1 || valueReleaseCount != 1) {
CFRelease(dict);
return -14;
}
CFDictionarySetValue(dict, key, valueNew);
if (keyReleaseCount != 2 || valueReleaseCount != 2) {
CFRelease(dict);
return -15;
}
if (valueRetainCount != 3) {
CFRelease(dict);
return -16;
}
count = CFDictionaryGetCount(dict);
if (count != 1) {
CFRelease(dict);
return -17;
}
retrievedValue = CFDictionaryGetValue(dict, key);
if (retrievedValue == NULL ||
strcmp((const char *)retrievedValue, valueNew) != 0) {
CFRelease(dict);
return -18;
}
if (keyReleaseCount != 2 || valueReleaseCount != 2) {
CFRelease(dict);
return -19;
}
CFDictionaryRemoveValue(dict, key);
if (keyReleaseCount != 3 || valueReleaseCount != 3) {
CFRelease(dict);
return -20;
}
count = CFDictionaryGetCount(dict);
if (count != 0) {
CFRelease(dict);
return -21;
}
retrievedValue = CFDictionaryGetValue(dict, key);
if (retrievedValue != NULL) {
CFRelease(dict);
return -22;
}
if (keyRetainCount != 3 || valueRetainCount != 3) {
CFRelease(dict);
return -23;
}
CFDictionaryAddValue(dict, key, value);
if (keyRetainCount != 4 || valueRetainCount != 4) {
CFRelease(dict);
return -24;
}
count = CFDictionaryGetCount(dict);
if (count != 1) {
CFRelease(dict);
return -25;
}
retrievedValue = CFDictionaryGetValue(dict, key);
if (retrievedValue == NULL ||
strcmp((const char *)retrievedValue, value) != 0) {
CFRelease(dict);
return -26;
}
count = CFDictionaryGetCount(dict);
if (count != 1) {
CFRelease(dict);
return -27;
}
const void **keys = malloc(sizeof(void *) * count);
const void **values = malloc(sizeof(void *) * count);
CFDictionaryGetKeysAndValues(dict, keys, values);
if (strcmp((const char *)keys[0], key) != 0 ||
strcmp((const char *)values[0], value) != 0) {
free(keys);
free(values);
CFRelease(dict);
return -28;
}
free(keys);
free(values);
if (keyReleaseCount != 3 || valueReleaseCount != 3) {
CFRelease(dict);
return -29;
}
CFDictionaryRemoveAllValues(dict);
if (keyReleaseCount != 4 || valueReleaseCount != 4) {
CFRelease(dict);
return -30;
}
count = CFDictionaryGetCount(dict);
if (count != 0) {
CFRelease(dict);
return -31;
}
// Check that value equality callback was not called (based on macOS behavior)
if (valueEqualCount != 0) {
CFRelease(dict);
return -32;
}
CFRelease(dict);
return 0;
}
// Counters for retain and release.
//
// We couldn't relay on the retainCounts of the objects directly
// as Objective-C retainCount method is meant to be for debug
// purposes only and modern versions are using tagged pointers anyway,
// thus return value of this method can be meaningless.
// Instead, we hook counter to the retain/release callbacks
// and check for changes in deltas
// (because actual counts could be different between implementations).
static int retainCount = 0;
static int releaseCount = 0;
// Callbacks similar to kCFTypeDictionaryKeyCallBacks and
// kCFTypeDictionaryValueCallBacks
const void *CFRetainWrapper(CFAllocatorRef allocator, const void *value) {
retainCount++;
return CFRetain(value);
}
void CFReleaseWrapper(CFAllocatorRef allocator, const void *value) {
releaseCount++;
CFRelease(value);
}
CFHashCode CFHashWrapper(const void *value) { return CFHash(value); }
Boolean CFEqualWrapper(const void *value1, const void *value2) {
return CFEqual(value1, value2);
}
CFDictionaryKeyCallBacks testDefaultKeyCallBacks = {
0, // version
CFRetainWrapper,
CFReleaseWrapper,
NULL, // stub of CFCopyDescription
CFEqualWrapper,
CFHashWrapper};
CFDictionaryValueCallBacks testDefaultValueCallBacks = {
0, // version
CFRetainWrapper, CFReleaseWrapper,
NULL, // stub of CFCopyDescription
CFEqualWrapper};
int test_CFMutableDictionary_CustomCallbacks_CFTypes() {
// Reset counters
retainCount = 0;
releaseCount = 0;
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(
NULL, 0, &testDefaultKeyCallBacks, &testDefaultValueCallBacks);
if (dict == NULL) {
return -1;
}
CFStringRef key =
CFStringCreateWithCString(NULL, "Key", kCFStringEncodingASCII);
CFStringRef value =
CFStringCreateWithCString(NULL, "Value", kCFStringEncodingASCII);
if (key == NULL || value == NULL) {
CFRelease(key);
CFRelease(value);
CFRelease(dict);
return -2;
}
// Create copies to be stored in the dictionary
CFStringRef key1 =
CFStringCreateWithCString(NULL, "Key", kCFStringEncodingASCII);
CFStringRef value1 =
CFStringCreateWithCString(NULL, "Value", kCFStringEncodingASCII);
int retainCountBefore = retainCount;
int releaseCountBefore = releaseCount;
CFDictionaryAddValue(dict, key1, value1);
int deltaRetain = retainCount - retainCountBefore;
int deltaRelease = releaseCount - releaseCountBefore;
// For the purpose of this test, we only care about delta between
// retain and release counts, e.g. receiving 1 retain and 1 release
// has the same net effect as receiving 2 retains and 2 releases,
// as delta for both of them is 0
int globalDelta = deltaRetain - deltaRelease;
if (globalDelta != 2) {
CFRelease(key);
CFRelease(value);
CFRelease(key1);
CFRelease(value1);
CFRelease(dict);
return -3;
}
// Release key1 and value1 since the dictionary has retained them
CFRelease(key1);
CFRelease(value1);
const void *retrievedValue = CFDictionaryGetValue(dict, key);
if (retrievedValue == NULL) {
CFRelease(key);
CFRelease(value);
CFRelease(dict);
return -4;
}
if (!CFEqual((CFStringRef)retrievedValue, value)) {
CFRelease(key);
CFRelease(value);
CFRelease(dict);
return -5;
}
CFStringRef valueNew =
CFStringCreateWithCString(NULL, "NewValue", kCFStringEncodingASCII);
if (valueNew == NULL) {
CFRelease(key);
CFRelease(value);
CFRelease(dict);
return -6;
}
retainCountBefore = retainCount;
releaseCountBefore = releaseCount;
CFDictionaryAddValue(dict, key, valueNew);
// Since the key already exists, the new value should not be added
deltaRetain = retainCount - retainCountBefore;
deltaRelease = releaseCount - releaseCountBefore;
globalDelta = deltaRetain - deltaRelease;
if (globalDelta != 0) {
CFRelease(key);
CFRelease(value);
CFRelease(valueNew);
CFRelease(dict);
return -7;
}
retrievedValue = CFDictionaryGetValue(dict, key);
if (!CFEqual((CFStringRef)retrievedValue, value)) {
CFRelease(key);
CFRelease(value);
CFRelease(valueNew);
CFRelease(dict);
return -8;
}
retainCountBefore = retainCount;
releaseCountBefore = releaseCount;
CFDictionarySetValue(dict, key, valueNew);
deltaRetain = retainCount - retainCountBefore;
deltaRelease = releaseCount - releaseCountBefore;
globalDelta = deltaRetain - deltaRelease;
if (globalDelta != 0) {
CFRelease(key);
CFRelease(value);
CFRelease(valueNew);
CFRelease(dict);
return -9;
}
retrievedValue = CFDictionaryGetValue(dict, key);
if (!CFEqual((CFStringRef)retrievedValue, valueNew)) {
CFRelease(key);
CFRelease(value);
CFRelease(valueNew);
CFRelease(dict);
return -10;
}
retainCountBefore = retainCount;
releaseCountBefore = releaseCount;
CFDictionaryRemoveValue(dict, key);
deltaRetain = retainCount - retainCountBefore;
deltaRelease = releaseCount - releaseCountBefore;
// The dictionary should release the key and value
// So delta should be -2
globalDelta = deltaRetain - deltaRelease;
if (globalDelta != -2) {
CFRelease(key);
CFRelease(value);
CFRelease(valueNew);
CFRelease(dict);
return -11;
}
retrievedValue = CFDictionaryGetValue(dict, key);
if (retrievedValue != NULL) {
CFRelease(key);
CFRelease(value);
CFRelease(valueNew);
CFRelease(dict);
return -12;
}
retainCountBefore = retainCount;
releaseCountBefore = releaseCount;
CFDictionaryAddValue(dict, key, value);
deltaRetain = retainCount - retainCountBefore;
deltaRelease = releaseCount - releaseCountBefore;
// The dictionary should retain the key and value
// So delta should be +2
globalDelta = deltaRetain - deltaRelease;
if (globalDelta != 2) {
CFRelease(key);
CFRelease(value);
CFRelease(valueNew);
CFRelease(dict);
return -13;
}
retrievedValue = CFDictionaryGetValue(dict, key);
if (!CFEqual((CFStringRef)retrievedValue, value)) {
CFRelease(key);
CFRelease(value);
CFRelease(valueNew);
CFRelease(dict);
return -14;
}
CFIndex count = CFDictionaryGetCount(dict);
if (count != 1) {
CFRelease(key);
CFRelease(value);
CFRelease(valueNew);
CFRelease(dict);
return -15;
}
const void **keys = malloc(sizeof(void *) * count);
const void **values = malloc(sizeof(void *) * count);
if (keys == NULL || values == NULL) {
free(keys);
free(values);
CFRelease(key);
CFRelease(value);
CFRelease(valueNew);
CFRelease(dict);
return -16;
}
CFDictionaryGetKeysAndValues(dict, keys, values);
if (!CFEqual((CFStringRef)keys[0], key) ||
!CFEqual((CFStringRef)values[0], value)) {
free(keys);
free(values);
CFRelease(key);
CFRelease(value);
CFRelease(valueNew);
CFRelease(dict);
return -17;
}
free(keys);
free(values);
retainCountBefore = retainCount;
releaseCountBefore = releaseCount;
CFDictionaryRemoveAllValues(dict);
deltaRetain = retainCount - retainCountBefore;
deltaRelease = releaseCount - releaseCountBefore;
// The dictionary should release the key and value
// So delta should be -2
globalDelta = deltaRetain - deltaRelease;
if (globalDelta != -2) {
CFRelease(key);
CFRelease(value);
CFRelease(valueNew);
CFRelease(dict);
return -18;
}
count = CFDictionaryGetCount(dict);
if (count != 0) {
CFRelease(key);
CFRelease(value);
CFRelease(valueNew);
CFRelease(dict);
return -19;
}
CFRelease(key);
CFRelease(value);
CFRelease(valueNew);
CFRelease(dict);
return 0;
}
int test_lrint() {
struct {
double input;
long int expected;
} test_cases[] = {
{0.0, 0L},
{0.5, 0L},
{1.0, 1L},
{1.5, 2L},
{2.0, 2L},
{2.5, 2L},
{3.0, 3L},
{3.5, 4L},
{4.5, 4L},
{5.5, 6L},
{-0.0, 0L},
{-0.5, 0L},
{-1.0, -1L},
{-1.5, -2L},
{-2.0, -2L},
{-2.5, -2L},
{-3.0, -3L},
{-3.5, -4L},
{-4.5, -4L},
{-5.5, -6L},
{1.4999999999, 1L},
{1.5000000001, 2L},
{-1.4999999999, -1L},
{-1.5000000001, -2L},
// Around INT_MAX
{2147483647.0, 2147483647L},
{2147483646.5, 2147483646L},
{2147483647.4, 2147483647L},
// Around INT_MIN
{-2147483648.0, -2147483648L},
{-2147483648.5, -2147483648L},
{-2147483647.5, -2147483648L},
};
int num_tests = sizeof(test_cases) / sizeof(test_cases[0]);
for (int i = 0; i < num_tests; i++) {
double input = test_cases[i].input;
long int expected = test_cases[i].expected;
long int result = lrint(input);
if (result != expected) {
return -(i + 1);
}
}
struct {
float input;
long int expected;
} test_cases_f[] = {
{0.0f, 0L},
{0.5f, 0L},
{1.0f, 1L},
{1.5f, 2L},
{2.0f, 2L},
{2.5f, 2L},
{3.0f, 3L},
{3.5f, 4L},
{4.5f, 4L},
{5.5f, 6L},
{-0.0f, 0L},
{-0.5f, 0L},
{-1.0f, -1L},
{-1.5f, -2L},
{-2.0f, -2L},
{-2.5f, -2L},
{-3.0f, -3L},
{-3.5f, -4L},
{-4.5f, -4L},
{-5.5f, -6L},
{1.4999999f, 1L},
{1.5000001f, 2L},
{-1.4999999f, -1L},
{-1.5000001f, -2L},
#ifdef DEFINE_ME_WHEN_BUILDING_ON_MACOS
// on macOS `long int` is 8 bytes
{2147483648.0f, 2147483648L},
#else
{2147483648.0f, 2147483647L}
#endif
};
int num_tests_f = sizeof(test_cases_f) / sizeof(test_cases_f[0]);
for (int i = 0; i < num_tests_f; i++) {
float input = test_cases_f[i].input;
long int expected = test_cases_f[i].expected;
long int result = lrintf(input);
if (result != expected) {
return -(num_tests + i + 1);
}
}
return 0;
}
int test_fesetround() {
int default_rounding = fegetround();
if (default_rounding != FE_TONEAREST) {
return -1;
}
if (lrint(+11.5) != +12.0 || lrint(+12.5) != +12.0 || lrint(-11.5) != -12.0) {
return -2;
}
int res = fesetround(FE_TOWARDZERO);
if (res != 0) {
return -3;
}
if (lrint(+11.5) != +11.0 || lrint(+12.5) != +12.0 || lrint(-11.5) != -11.0) {
return -4;
}
res = fesetround(default_rounding);
if (res != 0) {
return -5;
}
return 0;
}
int test_ldexp() {
struct {
double x;
int n;
double expected;
} test_cases[] = {
{0.0, 5, 0.0}, {-0.0, -3, -0.0}, {1.0, 0, 1.0}, {1.0, 1, 2.0},
{1.0, -1, 0.5}, {2.5, 3, 20.0}, {3.0, -2, 0.75},
};
int num_tests = sizeof(test_cases) / sizeof(test_cases[0]);
for (int i = 0; i < num_tests; i++) {
double x = test_cases[i].x;
int n = test_cases[i].n;
double expected = test_cases[i].expected;
double result = ldexp(x, n);
if (expected != result) {
return -(i + 1);
}
}
struct {
float x;
int n;
float expected;
} test_cases_f[] = {
{0.0f, 5, 0.0f}, {-0.0f, -3, -0.0f}, {1.0f, 0, 1.0f}, {1.0f, 1, 2.0f},
{1.0f, -1, 0.5f}, {2.5f, 3, 20.0f}, {3.0f, -2, 0.75f},
};
int num_tests_f = sizeof(test_cases_f) / sizeof(test_cases_f[0]);
for (int i = 0; i < num_tests_f; i++) {
float x = test_cases_f[i].x;
int n = test_cases_f[i].n;
float expected = test_cases_f[i].expected;
float result = ldexpf(x, n);
if (expected != result) {
return -(num_tests + i + 1);
}
}
return 0;
}
// Just for readability, similar to _CTYPE_* constants
#define MASK_RUNE_ALPHA 0x00100L
#define MASK_RUNE_CONTROL 0x00200L
#define MASK_RUNE_DIGIT 0x00400L
#define MASK_RUNE_GRAPH 0x00800L
#define MASK_RUNE_LOWER 0x01000L
#define MASK_RUNE_PUNCT 0x02000L
#define MASK_RUNE_SPACE 0x04000L
#define MASK_RUNE_UPPER 0x08000L
#define MASK_RUNE_XDIGIT 0x10000L
#define MASK_RUNE_BLANK 0x20000L
#define MASK_RUNE_PRINT 0x40000L
int test_maskrune() {
struct {
char c;
unsigned long mask;
int expected;
} test_cases[] = {
{'A', MASK_RUNE_ALPHA, 256}, {'A', MASK_RUNE_UPPER, 32768},
{'A', MASK_RUNE_GRAPH, 2048}, {'A', MASK_RUNE_LOWER, 0},
{'z', MASK_RUNE_ALPHA, 256}, {'z', MASK_RUNE_LOWER, 4096},
{'z', MASK_RUNE_GRAPH, 2048}, {'z', MASK_RUNE_UPPER, 0},
{'5', MASK_RUNE_DIGIT, 1024}, {'5', MASK_RUNE_XDIGIT, 65536},
{'5', MASK_RUNE_ALPHA, 0},
{'?', MASK_RUNE_PUNCT, 8192}, {'?', MASK_RUNE_GRAPH, 2048},
{'?', MASK_RUNE_PRINT, 262144}, {'?', MASK_RUNE_ALPHA, 0},
{' ', MASK_RUNE_SPACE, 16384}, {' ', MASK_RUNE_BLANK, 131072},
{' ', MASK_RUNE_PRINT, 262144}, {' ', MASK_RUNE_GRAPH, 0},
{'\n', MASK_RUNE_CONTROL, 512}, {'\n', MASK_RUNE_PRINT, 0},
{'\n', MASK_RUNE_GRAPH, 0},
{'F', MASK_RUNE_XDIGIT, 65536}, {'G', MASK_RUNE_XDIGIT, 0},
};
int num_tests = sizeof(test_cases) / sizeof(test_cases[0]);
for (int i = 0; i < num_tests; i++) {
char c = test_cases[i].c;
unsigned long mask = test_cases[i].mask;
int expected = test_cases[i].expected;
int result = __maskrune(c, mask);
if (expected != result) {
return -(i + 1);
}
}
return 0;
}
int test_frexpf(void) {
int exp_val;
float m;
/* Test 1: 8.0f = 0.5 * 2^4 */
m = frexpf(8.0f, &exp_val);
if (m != 0.5f || exp_val != 4)
return -1;
/* Test 2: 4.0f = 0.5 * 2^3 */
m = frexpf(4.0f, &exp_val);
if (m != 0.5f || exp_val != 3)
return -2;
/* Test 3: 0.75f is already normalized: 0.75f * 2^0 = 0.75f */
m = frexpf(0.75f, &exp_val);
if (m != 0.75f || exp_val != 0)
return -3;
/* Test 4: 1.0f = 0.5 * 2^1 */
m = frexpf(1.0f, &exp_val);
if (m != 0.5f || exp_val != 1)
return -4;
/* Test 5: 0.125f = 0.5 * 2^-2 */
m = frexpf(0.125f, &exp_val);
if (m != 0.5f || exp_val != -2)
return -5;
/* Test 6: 0.0f should return 0.0f and exponent 0 */
m = frexpf(0.0f, &exp_val);
if (m != 0.0f || exp_val != 0)
return -6;
/* Test 7: Negative value, -8.0f = -0.5 * 2^4 */
m = frexpf(-8.0f, &exp_val);
if (m != -0.5f || exp_val != 4)
return -7;
/* Test 8: -0.0f should be preserved (check with signbit) */
m = frexpf(-0.0f, &exp_val);
if (m != 0.0f || exp_val != 0)
return -8;
return 0;
}
int test_frexp() {
double value, frac;
int exp;
// Test 1: 0.0 -> should return 0.0 and exponent 0.
value = 0.0;
frac = frexp(value, &exp);
if (frac != 0.0 || exp != 0) {
return -1;
}
// Test 2: 8.0 -> 8.0 = 0.5 * 2^4, so fraction 0.5 and exponent 4.
value = 8.0;
frac = frexp(value, &exp);
if (frac != 0.5 || exp != 4) {
return -2;
}
// Test 3: 0.75 -> already normalized, should return 0.75 and exponent 0.
value = 0.75;
frac = frexp(value, &exp);
if (frac != 0.75 || exp != 0) {
return -3;
}
// Test 4: -4.0 -> -4.0 = -0.5 * 2^3, so fraction -0.5 and exponent 3.
value = -4.0;
frac = frexp(value, &exp);
if (frac != -0.5 || exp != 3) {
return -4;
}
// Test 5: 1.0 -> 1.0 = 0.5 * 2^1, so fraction 0.5 and exponent 1.
value = 1.0;
frac = frexp(value, &exp);
if (frac != 0.5 || exp != 1) {
return -5;
}
// Test 6: pi -> 3.141592653589793 = (pi/4) * 2^2, expect fraction
// ~0.7853981633974483 and exponent 2.
value = 3.141592653589793;
frac = frexp(value, &exp);
if (exp != 2 || fabs(frac - (3.141592653589793 / 4.0)) > 1e-15) {
return -6;
}
return 0;
}
void jmpfunction(jmp_buf env_buf) { longjmp(env_buf, 432); }
int test_setjmp() {
int val;
jmp_buf env_buffer;
/* save calling environment for longjmp */
val = setjmp(env_buffer);
if (val != 0) {
return val == 432 ? 0 : -2;
}
jmpfunction(env_buffer);
return -1;
}
int test_inet_addr() {
unsigned int res = inet_addr("127.0.0.1");
if (res != 16777343) {
return -1;
}
return 0;
}
int test_inet_ntop() {
struct in_addr addr;
char buffer[16]; // INET_ADDRSTRLEN
unsigned int res = inet_addr("127.0.0.1");
if (res != 16777343) {
return -1;
}
addr.s_addr = res;
if (inet_ntop(2, &addr, buffer, sizeof(buffer)) == NULL) {
return -2;
}
if (strcmp(buffer, "127.0.0.1") != 0) {
return -3;
}
return 0;
}
int test_inet_pton() {
const char *ip_str = "127.0.0.1";
struct in_addr addr;
int res = inet_pton(2, ip_str, &addr);
if (res <= 0) {
return -1;
}
if (addr.s_addr != 16777343) {
return -2;
}
return 0;
}
int test_case_CFURL(const char *basePathCStr, const char *urlPathCStr,
const char *fileNameCStr,
const char *expectedAppendedCStr) {
CFURLRef url = CFURLCreateFromFileSystemRepresentation(
NULL, (uint8_t *)urlPathCStr, strlen(urlPathCStr),
1 // isDirectory
);
if (url == NULL) {
return -1;
}
CFStringRef fileName =
CFStringCreateWithCString(NULL, fileNameCStr, kCFStringEncodingASCII);
CFURLRef appendedURL =
CFURLCreateCopyAppendingPathComponent(NULL, url, fileName,
0 // isDirectory
);
CFRelease(fileName);
if (appendedURL == NULL) {
CFRelease(url);
return -2;
}
CFStringRef gotPath =
CFURLCopyFileSystemPath(appendedURL, 0); // kCFURLPOSIXPathStyle
if (gotPath == NULL) {
CFRelease(appendedURL);
CFRelease(url);
return -3;
}
CFStringRef expectedAppended = CFStringCreateWithCString(
NULL, expectedAppendedCStr, kCFStringEncodingASCII);
if (!CFEqual(gotPath, expectedAppended)) {
CFRelease(expectedAppended);
CFRelease(gotPath);
CFRelease(appendedURL);
CFRelease(url);
return -4;
}
CFRelease(expectedAppended);
CFRelease(gotPath);
CFURLRef deletedURL =
CFURLCreateCopyDeletingLastPathComponent(NULL, appendedURL);
if (deletedURL == NULL) {
CFRelease(appendedURL);
CFRelease(url);
return -5;
}
gotPath = CFURLCopyFileSystemPath(deletedURL, 0); // kCFURLPOSIXPathStyle
if (gotPath == NULL) {
CFRelease(deletedURL);
CFRelease(appendedURL);
CFRelease(url);
return -6;
}
CFStringRef expectedBase =
CFStringCreateWithCString(NULL, basePathCStr, kCFStringEncodingASCII);
if (!CFEqual(gotPath, expectedBase)) {
CFRelease(expectedBase);
CFRelease(gotPath);
CFRelease(deletedURL);
CFRelease(appendedURL);
CFRelease(url);
return -7;
}
CFRelease(expectedBase);
CFRelease(gotPath);
CFRelease(deletedURL);
CFRelease(appendedURL);
CFRelease(url);
return 0;
}
int test_CFURL() {
// base path, url path, filename, expected path
int res = test_case_CFURL("/a/b/c", "/a/b/c", "test.txt", "/a/b/c/test.txt");
if (res != 0) {
return res;
}
res = test_case_CFURL("/a/b/c", "/a/b/c/", "test.txt", "/a/b/c/test.txt");
if (res != 0) {
return res - 10;
}
res = test_case_CFURL("/a/b/c", "/a/b/c/", "test.txt", "/a/b/c/test.txt");
if (res != 0) {
return res - 20;
}
return 0;
}
int test_CFNumberCompare_simple() {
float a = 3.333;
CFNumberRef aa = CFNumberCreate(NULL, 5, &a); // kCFNumberFloat32Type
double b = 3.333;
CFNumberRef bb = CFNumberCreate(NULL, 6, &b); // kCFNumberFloat64Type
CFComparisonResult res = CFNumberCompare(aa, bb, NULL);
// `3.333` looses precision as float, thus 2 numbers are not equal
if (res != kCFCompareLessThan) {
return -1;
}
res = CFNumberCompare(bb, aa, NULL);
if (res != kCFCompareGreaterThan) {
return -2;
}
int c = -1;
CFNumberRef cc = CFNumberCreate(NULL, 3, &c); // kCFNumberSInt32Type
long long d = -1;
CFNumberRef dd = CFNumberCreate(NULL, 4, &d); // kCFNumberSInt64Type
res = CFNumberCompare(cc, dd, NULL);
if (res != kCFCompareEqualTo) {
return -3;
}
char e = 0;
CFNumberRef ee = CFNumberCreate(NULL, 1, &e); // kCFNumberSInt8Type
double f = 0.0;
CFNumberRef ff = CFNumberCreate(NULL, 6, &f); // kCFNumberFloat64Type
res = CFNumberCompare(ee, ff, NULL);
if (res != kCFCompareEqualTo) {
return -4;
}
return 0;
}
#ifndef kCFNumberSInt8Type
#define kCFNumberSInt8Type 1
#define kCFNumberSInt16Type 2
#define kCFNumberSInt32Type 3
#define kCFNumberSInt64Type 4
#define kCFNumberFloat32Type 5
#define kCFNumberFloat64Type 6
#endif
static int cmp(CFNumberRef a, CFNumberRef b, CFComparisonResult expected,
const char *label, int failCode) {
CFComparisonResult r = CFNumberCompare(a, b, NULL);
if (r != expected) {
const char *expStr = expected == kCFCompareLessThan ? "<"
: expected == kCFCompareGreaterThan ? ">"
: "==";
const char *gotStr = r == kCFCompareLessThan ? "<"
: r == kCFCompareGreaterThan ? ">"
: "==";
printf("FAIL (%d): %s : expected %s, got %s\n", failCode, label, expStr,
gotStr);
return failCode;
}
return 0;
}
#define MAKE_NUM(var, typeEnum) CFNumberCreate(NULL, typeEnum, &(var))
#define TEST_CMP(aRef, bRef, expected, label, code) \
{ \
int _e = cmp(aRef, bRef, expected, label, code); \
if (_e) { \
CFRelease(aRef); \
CFRelease(bRef); \
return _e; \
} \
CFRelease(aRef); \
CFRelease(bRef); \
}
static int compare_integral_examples(void) {
/* Cross-width equalities */
{
int32_t v32 = -1;
int64_t v64 = -1;
CFNumberRef n32 = MAKE_NUM(v32, kCFNumberSInt32Type);
CFNumberRef n64 = MAKE_NUM(v64, kCFNumberSInt64Type);
TEST_CMP(n32, n64, kCFCompareEqualTo, "SInt32 -1 == SInt64 -1", -10);
}
{
int8_t z8 = 0;
double zD = 0.0;
CFNumberRef n8 = MAKE_NUM(z8, kCFNumberSInt8Type);
CFNumberRef nD = MAKE_NUM(zD, kCFNumberFloat64Type);
TEST_CMP(n8, nD, kCFCompareEqualTo, "SInt8 0 == Float64 0.0", -11);
}
/* Min / Max ordering across widths */
{
int64_t min64 = INT64_MIN;
int32_t min32 = INT32_MIN;
CFNumberRef n64 = MAKE_NUM(min64, kCFNumberSInt64Type);
CFNumberRef n32 = MAKE_NUM(min32, kCFNumberSInt32Type);
TEST_CMP(n64, n32, kCFCompareLessThan, "INT64_MIN < INT32_MIN", -12);
}
{
int64_t max64 = INT64_MAX;
int32_t max32 = INT32_MAX;
CFNumberRef n64 = MAKE_NUM(max64, kCFNumberSInt64Type);
CFNumberRef n32 = MAKE_NUM(max32, kCFNumberSInt32Type);
TEST_CMP(n64, n32, kCFCompareGreaterThan, "INT64_MAX > INT32_MAX", -13);
}
{
int16_t min16 = INT16_MIN; /* -32768 */
int8_t min8 = INT8_MIN; /* -128 */
CFNumberRef n16 = MAKE_NUM(min16, kCFNumberSInt16Type);
CFNumberRef n8 = MAKE_NUM(min8, kCFNumberSInt8Type);
TEST_CMP(n16, n8, kCFCompareLessThan, "INT16_MIN < INT8_MIN", -14);
}
{
int16_t max16 = INT16_MAX;
int8_t max8 = INT8_MAX;
CFNumberRef n16 = MAKE_NUM(max16, kCFNumberSInt16Type);
CFNumberRef n8 = MAKE_NUM(max8, kCFNumberSInt8Type);
TEST_CMP(n16, n8, kCFCompareGreaterThan, "INT16_MAX > INT8_MAX", -15);
}
/* Extremes vs -1 */
{
int64_t min64 = INT64_MIN;
int64_t neg1 = -1;
CFNumberRef nMin = MAKE_NUM(min64, kCFNumberSInt64Type);
CFNumberRef nNeg1 = MAKE_NUM(neg1, kCFNumberSInt64Type);
TEST_CMP(nMin, nNeg1, kCFCompareLessThan, "INT64_MIN < -1", -16);
}
return 0;
}
static int compare_precision_examples(void) {
/* Original float vs double 3.333 */
{
float f = 3.333f;
double d = 3.333;
CFNumberRef nf = MAKE_NUM(f, kCFNumberFloat32Type);
CFNumberRef nd = MAKE_NUM(d, kCFNumberFloat64Type);
/* float loses precision => float < double (expected) */
TEST_CMP(nf, nd, kCFCompareLessThan, "float 3.333f < double 3.333", -20);
/* Reverse */
float f2 = 3.333f;
double d2 = 3.333;
CFNumberRef nf2 = MAKE_NUM(f2, kCFNumberFloat32Type);
CFNumberRef nd2 = MAKE_NUM(d2, kCFNumberFloat64Type);
TEST_CMP(nd2, nf2, kCFCompareGreaterThan, "double 3.333 > float 3.333f",
-21);
}
/* 0.1f vs 0.1 (0.1f rounds *up* relative to double literal 0.1) */
{
float f = 0.1f;
double d = 0.1; /* double literal */
CFNumberRef nf = MAKE_NUM(f, kCFNumberFloat32Type);
CFNumberRef nd = MAKE_NUM(d, kCFNumberFloat64Type);
/* 0.1f (promoted) is slightly greater than 0.1 double */
TEST_CMP(nf, nd, kCFCompareGreaterThan, "0.1f > 0.1 (double)", -22);
}
/* INT64_MAX vs its double representation (double rounds) */
{
int64_t i = INT64_MAX; /* 9223372036854775807 */
double d = (double)INT64_MAX; /* Rounds to 9223372036854775808 */
CFNumberRef ni = MAKE_NUM(i, kCFNumberSInt64Type);
CFNumberRef nd = MAKE_NUM(d, kCFNumberFloat64Type);
TEST_CMP(ni, nd, kCFCompareLessThan,
"INT64_MAX (exact) < double(INT64_MAX) (rounded up)", -23);
}
return 0;
}
static int compare_special_float_values(void) {
/* Positive vs negative zero */
{
double pz = 0.0;
double nz = -0.0;
CFNumberRef nP = MAKE_NUM(pz, kCFNumberFloat64Type);
CFNumberRef nN = MAKE_NUM(nz, kCFNumberFloat64Type);
TEST_CMP(nP, nN, kCFCompareEqualTo, "+0.0 == -0.0", -24);
}
/* Infinities */
{
double inf = INFINITY;
double ninf = -INFINITY;
double zero = 0.0;
CFNumberRef nInf = MAKE_NUM(inf, kCFNumberFloat64Type);
CFNumberRef nZero = MAKE_NUM(zero, kCFNumberFloat64Type);
TEST_CMP(nInf, nZero, kCFCompareGreaterThan, "Inf > 0", -25);
nZero = MAKE_NUM(zero, kCFNumberFloat64Type);
CFNumberRef nNInf = MAKE_NUM(ninf, kCFNumberFloat64Type);
TEST_CMP(nNInf, nZero, kCFCompareLessThan, "-Inf < 0", -26);
nInf = MAKE_NUM(inf, kCFNumberFloat64Type);
nNInf = MAKE_NUM(ninf, kCFNumberFloat64Type);
TEST_CMP(nInf, nNInf, kCFCompareGreaterThan, "Inf > -Inf", -27);
}
return 0;
}
static int compare_unsigned_limit_examples(void) {
/* UINT64_MAX cannot be stored exactly as a signed 64-bit CFNumber.
We *demonstrate* by comparing a double approximation vs INT64_MAX. */
{
double u64d = (double)UINT64_MAX; /* ~1.844674407e19 (loses low bits) */
int64_t i64max = INT64_MAX; /* 9.223372036854775807e18 */
CFNumberRef nUApprox = MAKE_NUM(u64d, kCFNumberFloat64Type);
CFNumberRef nI64Max = MAKE_NUM(i64max, kCFNumberSInt64Type);
TEST_CMP(nUApprox, nI64Max, kCFCompareGreaterThan,
"double(UINT64_MAX) > INT64_MAX", -28);
}
/* Similar for smaller widths: compare UINT32_MAX via double vs INT32_MAX
* (exact vs rounding) */
{
double u32d = (double)UINT32_MAX; /* 4294967295 exactly representable */
int32_t s32max = INT32_MAX; /* 2147483647 */
CFNumberRef nU = MAKE_NUM(u32d, kCFNumberFloat64Type);
CFNumberRef nS = MAKE_NUM(s32max, kCFNumberSInt32Type);
TEST_CMP(nU, nS, kCFCompareGreaterThan, "double(UINT32_MAX) > INT32_MAX",
-29);
}
/* UINT8_MAX vs INT8_MAX using a wider signed container (int16) for 255 */
{
int16_t u8max_as16 = 255; /* representable */
int8_t s8max = INT8_MAX; /* 127 */
CFNumberRef nU = MAKE_NUM(u8max_as16, kCFNumberSInt16Type);
CFNumberRef nS = MAKE_NUM(s8max, kCFNumberSInt8Type);
TEST_CMP(nU, nS, kCFCompareGreaterThan, "255 (as SInt16) > INT8_MAX", -30);
}
return 0;
}
int test_CFNumberCompare_extended(void) {
int r;
r = compare_integral_examples();
if (r)
return r;
r = compare_precision_examples();
if (r)
return r;
r = compare_special_float_values();
if (r)
return r;
r = compare_unsigned_limit_examples();
if (r)
return r;
return 0;
}
int test_memset_pattern() {
char buf[64];
// memset_pattern4
memset_pattern4(buf, "1234", sizeof(buf));
if (strncmp(buf, "1234123412", 10) != 0) {
return -1;
}
memset(buf, 0, sizeof(buf));
memset_pattern4(buf, "abcd", 8);
if (memcmp(buf, "abcdabcd", 8) != 0) {
return -2;
}
memset(buf, 0, sizeof(buf));
memset_pattern4(buf, "XYZW", 3);
if (memcmp(buf, "XYZ", 3) != 0) {
return -3;
}
char original_buf[sizeof(buf)];
memset(buf, 0xAA, sizeof(buf)); // Fill buffer with a known value
memcpy(original_buf, buf, sizeof(buf));
memset_pattern4(buf, "1234", 0);
if (memcmp(buf, original_buf, sizeof(buf)) != 0) {
return -4;
}
memset(buf, 0, sizeof(buf));
char pattern4_null[] = {'A', '\0', 'B', 'C'};
char expected4_null[] = {'A', '\0', 'B', 'C', 'A', '\0', 'B'};
memset_pattern4(buf, pattern4_null, 7);
if (memcmp(buf, expected4_null, 7) != 0) {
return -5;
}
// memset_pattern8
unsigned long long pattern8 = 0x0102030405060708;
char expected8_full[] = "\x08\x07\x06\x05\x04\x03\x02\x01";
memset(buf, 0, sizeof(buf));
memset_pattern8(buf, &pattern8, 10);
if (memcmp(buf, expected8_full, 8) != 0 ||
memcmp(buf + 8, expected8_full, 2) != 0) {
return -6;
}
memset(buf, 0, sizeof(buf));
memset_pattern8(buf, &pattern8, 16);
if (memcmp(buf, expected8_full, 8) != 0 ||
memcmp(buf + 8, expected8_full, 8) != 0) {
return -7;
}
memset(buf, 0, sizeof(buf));
memset_pattern8(buf, &pattern8, 5);
if (memcmp(buf, expected8_full, 5) != 0) {
return -8;
}
// memset_pattern16
const char *pattern16 = "0123456789ABCDEF";
memset(buf, 0, sizeof(buf));
memset_pattern16(buf, pattern16, 20);
char expected16_trunc[] = "0123456789ABCDEF0123";
if (memcmp(buf, expected16_trunc, 20) != 0) {
return -9;
}
memset(buf, 0, sizeof(buf));
memset_pattern16(buf, pattern16, 32);
char expected16_exact[] = "0123456789ABCDEF0123456789ABCDEF";
if (memcmp(buf, expected16_exact, 32) != 0) {
return -10;
}
return 0;
}
typedef struct {
SyncTester *tester;
BOOL res;
} sync_test_arg;
void *modify(sync_test_arg *arg) {
SyncTester *tester = arg->tester;
arg->res = [tester holdAndCheckCounter];
return NULL;
}
void *try_modify(SyncTester *tester) {
[tester tryModifyCounter];
return NULL;
}
int test_synchronized() {
SyncTester *sync_test = [SyncTester new];
sync_test_arg *arg = malloc(sizeof(sync_test_arg));
memset(arg, 0, sizeof(sync_test_arg));
arg->tester = sync_test;
pthread_t locking_thread;
pthread_create(&locking_thread, NULL, (void *(*)(void *)) & modify, arg);
pthread_t blocked_threads[10];
for (int i = 0; i < 10; i++) {
pthread_create(blocked_threads + i, NULL, (void *(*)(void *)) & try_modify,
sync_test);
}
if (pthread_join(locking_thread, NULL))
return -1;
if (!arg->res)
return -1;
[sync_test recursiveSyncEnter];
if (!sync_test.test_ok)
return -1;
return 0;
}
bool test_case_CFURLHasDirectoryPath(const char *str) {
CFURLRef url = CFURLCreateWithBytes(NULL, str, strlen(str),
kCFStringEncodingASCII, NULL);
if (!url) {
return false;
}
Boolean res = CFURLHasDirectoryPath(url);
CFRelease(url);
return res;
}
int test_CFURLHasDirectoryPath() {
if (test_case_CFURLHasDirectoryPath("/a/b"))
return -1;
if (!test_case_CFURLHasDirectoryPath("/a/b/"))
return -2;
if (!test_case_CFURLHasDirectoryPath("/"))
return -3;
if (test_case_CFURLHasDirectoryPath("//"))
return -4;
if (test_case_CFURLHasDirectoryPath("//a"))
return -5;
if (!test_case_CFURLHasDirectoryPath("//a/"))
return -6;
if (!test_case_CFURLHasDirectoryPath("///"))
return -7;
if (!test_case_CFURLHasDirectoryPath("////"))
return -8;
if (!test_case_CFURLHasDirectoryPath("."))
return -9;
if (!test_case_CFURLHasDirectoryPath(".."))
return -10;
if (test_case_CFURLHasDirectoryPath("..."))
return -11;
if (!test_case_CFURLHasDirectoryPath("/.."))
return -12;
if (test_case_CFURLHasDirectoryPath(""))
return -13;
return 0;
}
int test_NSMutableString_deleteCharactersInRange() {
NSAutoreleasePool *pool = [NSAutoreleasePool new];
NSMutableString *str = [NSMutableString stringWithUTF8String:"abc"];
NSRange r1 = {0, 3};
[str deleteCharactersInRange:r1];
NSString *expected = [NSString stringWithUTF8String:""];
if (!CFEqual(str, expected)) {
return -1;
}
str = [NSMutableString stringWithUTF8String:"abc"];
NSRange r2 = {1, 1};
[str deleteCharactersInRange:r2];
expected = [NSString stringWithUTF8String:"ac"];
if (!CFEqual(str, expected)) {
return -2;
}
str = [NSMutableString stringWithUTF8String:"abc"];
NSRange r3 = {0, 2};
[str deleteCharactersInRange:r3];
expected = [NSString stringWithUTF8String:"c"];
if (!CFEqual(str, expected)) {
return -3;
}
[pool drain];
return 0;
}
// clang-format off
#define FUNC_DEF(func) \
{ &func, #func }
struct {
int (*func)();
const char *name;
} test_func_array[] = {
#ifndef DEFINE_ME_WHEN_BUILDING_ON_MACOS
// below tests are failing on macOS,
// so we skip them
FUNC_DEF(test_getcwd_chdir),
FUNC_DEF(test_synchronized),
FUNC_DEF(test_read_directory_as_fd),
#endif
FUNC_DEF(test_qsort),
FUNC_DEF(test_vsnprintf),
FUNC_DEF(test_sscanf),
FUNC_DEF(test_swscanf),
FUNC_DEF(test_realloc),
FUNC_DEF(test_atof),
FUNC_DEF(test_strtof),
FUNC_DEF(test_sem),
FUNC_DEF(test_mtsem),
FUNC_DEF(test_CGAffineTransform),
FUNC_DEF(test_strncpy),
FUNC_DEF(test_strncat),
FUNC_DEF(test_strlcpy),
FUNC_DEF(test_setlocale),
FUNC_DEF(test_strtoul),
FUNC_DEF(test_strtol),
FUNC_DEF(test_dirent),
FUNC_DEF(test_scandir),
FUNC_DEF(test_strchr),
FUNC_DEF(test_swprintf),
FUNC_DEF(test_realpath),
FUNC_DEF(test_ungetc),
FUNC_DEF(test_fscanf),
FUNC_DEF(test_fscanf_new),
FUNC_DEF(test_CFStringFind),
FUNC_DEF(test_strcspn),
FUNC_DEF(test_mbstowcs),
FUNC_DEF(test_CFMutableString),
FUNC_DEF(test_fwrite),
FUNC_DEF(test_open),
FUNC_DEF(test_close),
FUNC_DEF(test_cond_var),
FUNC_DEF(test_CFMutableDictionary_NullCallbacks),
FUNC_DEF(test_CFMutableDictionary_CustomCallbacks_PrimitiveTypes),
FUNC_DEF(test_CFMutableDictionary_CustomCallbacks_CFTypes),
FUNC_DEF(test_lrint),
FUNC_DEF(test_fesetround),
FUNC_DEF(test_ldexp),
FUNC_DEF(test_maskrune),
FUNC_DEF(test_frexpf),
FUNC_DEF(test_frexp),
FUNC_DEF(test_setjmp),
FUNC_DEF(test_inet_addr),
FUNC_DEF(test_inet_ntop),
FUNC_DEF(test_inet_pton),
FUNC_DEF(test_CFURL),
FUNC_DEF(test_CFNumberCompare_simple),
FUNC_DEF(test_CFNumberCompare_extended),
FUNC_DEF(test_memset_pattern),
FUNC_DEF(test_CGGeometry),
FUNC_DEF(test_CFURLHasDirectoryPath),
FUNC_DEF(test_CGImage_JPEG),
FUNC_DEF(test_NSMutableString_deleteCharactersInRange),
};
// clang-format on
int TestApp_cli_tests_main(void) {
#ifdef DEFINE_ME_WHEN_BUILDING_ON_MACOS
setbuf(stdout, NULL);
#endif
int tests_run = 0;
int tests_passed = 0;
int n = sizeof(test_func_array) / sizeof(test_func_array[0]);
int i;
for (i = 0; i < n; i++) {
printf("%s: ", test_func_array[i].name);
tests_run++;
int latest_test_result = test_func_array[i].func();
if (latest_test_result == 0) {
printf("OK\n");
tests_passed++;
} else {
printf("FAIL (%d)\n", latest_test_result);
}
}
printf("Passed %d out of %d tests\n", tests_passed, tests_run);
return tests_run == tests_passed ? 0 : 1;
}