mirror of
https://github.com/touchHLE/touchHLE.git
synced 2026-01-31 01:25:24 +01:00
3225 lines
84 KiB
Objective-C
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;
|
|
}
|