mirror of https://gitlab.com/nakst/essence
1418 lines
48 KiB
C++
1418 lines
48 KiB
C++
#ifdef API_TESTS_FOR_RUNNER
|
|
|
|
#define TEST(_callback, _timeoutSeconds) { .cName = #_callback, .timeoutSeconds = _timeoutSeconds }
|
|
typedef struct Test { const char *cName; int timeoutSeconds; } Test;
|
|
|
|
#else
|
|
|
|
#define ES_PRIVATE_APIS
|
|
#include <essence.h>
|
|
#include <shared/crc.h>
|
|
#include <shared/array.cpp>
|
|
#include <shared/arena.cpp>
|
|
#include <shared/range_set.cpp>
|
|
#undef EsUTF8IsValid
|
|
#include <shared/unicode.cpp>
|
|
|
|
#define TEST(_callback, _timeoutSeconds) { .callback = _callback }
|
|
struct Test { bool (*callback)(); };
|
|
|
|
#define CHECK(x) do { if ((x)) { checkIndex++; } else { EsPrint("Failed check %d: " #x, checkIndex); return false; } } while (0)
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
bool AreFloatsRoughlyEqual(float a, float b) { return a - b < 0.0001f && a - b > -0.0001f; }
|
|
bool AreDoublesRoughlyEqual(double a, double b) { return a - b < 0.00000000001 && a - b > -0.00000000001; }
|
|
bool AreFloatsRoughlyEqual2(float a, float b) { return a - b < 0.01f && a - b > -0.01f; }
|
|
|
|
int CompareU8(const void *_a, const void *_b) {
|
|
uint8_t a = *(const uint8_t *) _a, b = *(const uint8_t *) _b;
|
|
return a < b ? -1 : a > b;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
bool BasicFileOperationsDoByteCount(size_t byteCount) {
|
|
uint8_t *buffer = (uint8_t *) EsHeapAllocate(byteCount, false);
|
|
|
|
for (uintptr_t i = 0; i < byteCount; i++) {
|
|
buffer[i] = EsRandomU8();
|
|
}
|
|
|
|
EsError error = EsFileWriteAll(EsLiteral("|Settings:/temp.dat"), buffer, byteCount);
|
|
|
|
if (error != ES_SUCCESS) {
|
|
EsPrint("Error %d writing file of size %d.\n", error, byteCount);
|
|
return false;
|
|
}
|
|
|
|
size_t readSize;
|
|
uint8_t *read = (uint8_t *) EsFileReadAll(EsLiteral("|Settings:/temp.dat"), &readSize, &error);
|
|
|
|
if (error != ES_SUCCESS) {
|
|
EsPrint("Error %d reading file of size %d.\n", error, byteCount);
|
|
return false;
|
|
}
|
|
|
|
if (readSize != byteCount) {
|
|
EsPrint("Read size mismatch: got %d, expected %d.\n", readSize, byteCount);
|
|
return false;
|
|
}
|
|
|
|
if (EsMemoryCompare(buffer, read, byteCount)) {
|
|
EsPrint("Read data mismatch.\n");
|
|
return false;
|
|
}
|
|
|
|
EsHeapFree(buffer);
|
|
EsHeapFree(read);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool BasicFileOperations() {
|
|
int checkIndex = 0;
|
|
|
|
for (uintptr_t i = 0; i < 24; i += 2) {
|
|
CHECK(BasicFileOperationsDoByteCount(1 << i));
|
|
}
|
|
|
|
for (uintptr_t i = 18; i > 0; i -= 3) {
|
|
CHECK(BasicFileOperationsDoByteCount(1 << i));
|
|
}
|
|
|
|
EsError error = EsPathDelete(EsLiteral("|Settings:/temp.dat"));
|
|
|
|
if (error != ES_SUCCESS) {
|
|
EsPrint("Error %d deleting file.\n", error);
|
|
CHECK(false);
|
|
}
|
|
|
|
EsFileReadAll(EsLiteral("|Settings:/temp.dat"), nullptr, &error);
|
|
|
|
if (error != ES_ERROR_FILE_DOES_NOT_EXIST) {
|
|
EsPrint("Checking file does not exist after deleting, instead got error %d.\n", error);
|
|
CHECK(false);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
bool CRTMathFunctions() {
|
|
int checkIndex = 0;
|
|
|
|
for (int i = 0; i <= 5; i++) CHECK(EsCRTabs(i) == i);
|
|
for (int i = -5; i <= 0; i++) CHECK(EsCRTabs(i) == -i);
|
|
|
|
for (float i = -1.0f; i <= 1.0f; i += 0.01f) CHECK(AreFloatsRoughlyEqual(i, EsCRTcosf(EsCRTacosf(i))));
|
|
for (float i = 0.0f; i <= 10.0f; i += 0.1f) CHECK(AreFloatsRoughlyEqual(EsCRTcosf(i), EsCRTcosf(-i)));
|
|
for (float i = 0.0f; i <= 10.0f; i += 0.1f) CHECK(AreFloatsRoughlyEqual(EsCRTcosf(i), EsCRTcosf(i + 2 * ES_PI)));
|
|
for (double i = 0.0; i <= 10.0; i += 0.1) CHECK(AreDoublesRoughlyEqual(EsCRTcos(i), EsCRTcos(-i)));
|
|
for (double i = 0.0; i <= 10.0; i += 0.1) CHECK(AreDoublesRoughlyEqual(EsCRTcos(i), EsCRTcos(i + 2 * ES_PI)));
|
|
for (double i = 0.0; i <= 10.0; i += 0.1) CHECK(AreFloatsRoughlyEqual(EsCRTcos(i), EsCRTcosf(i)));
|
|
CHECK(AreFloatsRoughlyEqual(EsCRTacosf(-1.0f), ES_PI));
|
|
CHECK(AreFloatsRoughlyEqual(EsCRTacosf(0.0f), ES_PI / 2.0f));
|
|
CHECK(AreFloatsRoughlyEqual(EsCRTacosf(1.0f), 0.0f));
|
|
CHECK(AreFloatsRoughlyEqual(EsCRTcosf(0.0f), 1.0f));
|
|
CHECK(AreFloatsRoughlyEqual(EsCRTcosf(ES_PI / 2.0f), 0.0f));
|
|
CHECK(AreFloatsRoughlyEqual(EsCRTcosf(ES_PI), -1.0f));
|
|
CHECK(AreDoublesRoughlyEqual(EsCRTcos(0.0), 1.0));
|
|
CHECK(AreDoublesRoughlyEqual(EsCRTcos(ES_PI / 2.0), 0.0));
|
|
CHECK(AreDoublesRoughlyEqual(EsCRTcos(ES_PI), -1.0));
|
|
|
|
for (float i = -1.0f; i <= 1.0f; i += 0.01f) CHECK(AreFloatsRoughlyEqual(i, EsCRTsinf(EsCRTasinf(i))));
|
|
for (float i = 0.0f; i <= 10.0f; i += 0.1f) CHECK(AreFloatsRoughlyEqual(EsCRTsinf(i), -EsCRTsinf(-i)));
|
|
for (float i = 0.0f; i <= 10.0f; i += 0.1f) CHECK(AreFloatsRoughlyEqual(EsCRTsinf(i), EsCRTsinf(i + 2 * ES_PI)));
|
|
for (double i = 0.0; i <= 10.0; i += 0.1) CHECK(AreDoublesRoughlyEqual(EsCRTsin(i), -EsCRTsin(-i)));
|
|
for (double i = 0.0; i <= 10.0; i += 0.1) CHECK(AreDoublesRoughlyEqual(EsCRTsin(i), EsCRTsin(i + 2 * ES_PI)));
|
|
for (double i = 0.0; i <= 10.0; i += 0.1) CHECK(AreFloatsRoughlyEqual(EsCRTsin(i), EsCRTsinf(i)));
|
|
CHECK(AreFloatsRoughlyEqual(EsCRTasinf(-1.0f), ES_PI / -2.0f));
|
|
CHECK(AreFloatsRoughlyEqual(EsCRTasinf(0.0f), 0.0f));
|
|
CHECK(AreFloatsRoughlyEqual(EsCRTasinf(1.0f), ES_PI / 2.0f));
|
|
CHECK(AreFloatsRoughlyEqual(EsCRTsinf(0.0f), 0.0f));
|
|
CHECK(AreFloatsRoughlyEqual(EsCRTsinf(ES_PI / 2.0f), 1.0f));
|
|
CHECK(AreFloatsRoughlyEqual(EsCRTsinf(ES_PI), 0.0f));
|
|
CHECK(AreDoublesRoughlyEqual(EsCRTsin(0.0), 0.0));
|
|
CHECK(AreDoublesRoughlyEqual(EsCRTsin(ES_PI / 2.0), 1.0));
|
|
CHECK(AreDoublesRoughlyEqual(EsCRTsin(ES_PI), 0.0));
|
|
|
|
for (float i = 0.0f; i <= 5.0f; i += 0.1f) CHECK(AreFloatsRoughlyEqual(EsCRTsqrtf(i) * EsCRTsqrtf(i), i));
|
|
for (float i = -5.0f; i <= 5.0f; i += 0.1f) CHECK(AreFloatsRoughlyEqual(EsCRTcbrtf(i) * EsCRTcbrtf(i) * EsCRTcbrtf(i), i));
|
|
for (double i = 0.0; i <= 5.0; i += 0.1) CHECK(AreDoublesRoughlyEqual(EsCRTsqrt(i) * EsCRTsqrt(i), i));
|
|
for (double i = -5.0; i <= 5.0; i += 0.1) CHECK(AreDoublesRoughlyEqual(EsCRTcbrt(i) * EsCRTcbrt(i) * EsCRTcbrt(i), i));
|
|
|
|
for (float i = -5.0f; i <= 0.0f; i += 0.01f) CHECK(-i == EsCRTfabsf(i));
|
|
for (float i = 0.0f; i <= 5.0f; i += 0.01f) CHECK(i == EsCRTfabsf(i));
|
|
for (double i = -5.0; i <= 0.0; i += 0.01) CHECK(-i == EsCRTfabs(i));
|
|
for (double i = 0.0; i <= 5.0; i += 0.01) CHECK(i == EsCRTfabs(i));
|
|
|
|
// This tests avoid angles near the y axis with atan2, because y/x blows up there. TODO Is this acceptable behaviour?
|
|
for (float i = -ES_PI / 4.0f; i < ES_PI / 4.0f; i += 0.01f) CHECK(AreFloatsRoughlyEqual(i, EsCRTatan2f(EsCRTsinf(i), EsCRTcosf(i))));
|
|
for (float i = -ES_PI / 4.0f; i < ES_PI / 4.0f; i += 0.01f) CHECK(AreFloatsRoughlyEqual(i, EsCRTatanf(EsCRTsinf(i) / EsCRTcosf(i))));
|
|
for (float i = -ES_PI * 7.0f / 8.0f; i < -ES_PI * 5.0f / 8.0f; i += 0.01f) CHECK(AreFloatsRoughlyEqual(i, EsCRTatan2f(EsCRTsinf(i), EsCRTcosf(i))));
|
|
for (float i = ES_PI * 5.0f / 8.0f; i < ES_PI * 7.0f / 8.0f; i += 0.01f) CHECK(AreFloatsRoughlyEqual(i, EsCRTatan2f(EsCRTsinf(i), EsCRTcosf(i))));
|
|
for (float i = -2.25f * ES_PI; i < -1.75f * ES_PI; i += 0.01f) CHECK(AreFloatsRoughlyEqual(i + 2 * ES_PI, EsCRTatan2f(EsCRTsinf(i), EsCRTcosf(i))));
|
|
for (float i = 1.75f * ES_PI; i < 2.25f * ES_PI; i += 0.01f) CHECK(AreFloatsRoughlyEqual(i - 2 * ES_PI, EsCRTatan2f(EsCRTsinf(i), EsCRTcosf(i))));
|
|
for (float i = -2.25f * ES_PI; i < -1.75f * ES_PI; i += 0.01f) CHECK(AreFloatsRoughlyEqual(i + 2 * ES_PI, EsCRTatanf(EsCRTsinf(i) / EsCRTcosf(i))));
|
|
for (float i = 1.75f * ES_PI; i < 2.25f * ES_PI; i += 0.01f) CHECK(AreFloatsRoughlyEqual(i - 2 * ES_PI, EsCRTatanf(EsCRTsinf(i) / EsCRTcosf(i))));
|
|
for (double i = -ES_PI / 4.0; i < ES_PI / 4.0; i += 0.01) CHECK(AreDoublesRoughlyEqual(i, EsCRTatan2(EsCRTsin(i), EsCRTcos(i))));
|
|
for (double i = -ES_PI * 7.0 / 8.0; i < -ES_PI * 5.0 / 8.0; i += 0.01) CHECK(AreDoublesRoughlyEqual(i, EsCRTatan2(EsCRTsin(i), EsCRTcos(i))));
|
|
for (double i = ES_PI * 5.0 / 8.0; i < ES_PI * 7.0 / 8.0; i += 0.01) CHECK(AreDoublesRoughlyEqual(i, EsCRTatan2(EsCRTsin(i), EsCRTcos(i))));
|
|
for (double i = -2.25 * ES_PI; i < -1.75 * ES_PI; i += 0.01) CHECK(AreDoublesRoughlyEqual(i + 2 * ES_PI, EsCRTatan2(EsCRTsin(i), EsCRTcos(i))));
|
|
for (double i = 1.75 * ES_PI; i < 2.25 * ES_PI; i += 0.01) CHECK(AreDoublesRoughlyEqual(i - 2 * ES_PI, EsCRTatan2(EsCRTsin(i), EsCRTcos(i))));
|
|
|
|
for (float i = -1000.0f; i <= 1000.0f; i += 1.0f) CHECK(EsCRTceilf(i) == i);
|
|
for (float i = -1000.0f; i <= 1000.0f; i += 1.0f) CHECK(EsCRTfloorf(i) == i);
|
|
for (float i = -1000.0f; i <= 1000.0f; i += 1.0f) CHECK(EsCRTceilf(i - 0.000001f) == i);
|
|
for (float i = -1000.0f; i <= 1000.0f; i += 1.0f) CHECK(EsCRTfloorf(i + 0.000001f) == i);
|
|
for (float i = -1000.0f; i <= 1000.0f; i += 1.0f) CHECK(EsCRTceilf(i - 0.1f) == i);
|
|
for (float i = -1000.0f; i <= 1000.0f; i += 1.0f) CHECK(EsCRTfloorf(i + 0.1f) == i);
|
|
for (float i = -1000.0f; i <= 1000.0f; i += 1.0f) CHECK(EsCRTceilf(i - 0.5f) == i);
|
|
for (float i = -1000.0f; i <= 1000.0f; i += 1.0f) CHECK(EsCRTfloorf(i + 0.5f) == i);
|
|
for (float i = -1000.0f; i <= 1000.0f; i += 1.0f) CHECK(EsCRTceilf(i - 0.9f) == i);
|
|
for (float i = -1000.0f; i <= 1000.0f; i += 1.0f) CHECK(EsCRTfloorf(i + 0.9f) == i);
|
|
for (float i = -1000.0f; i <= 1000.0f; i += 1.0f) CHECK(EsCRTceilf(i - 0.1f) == EsCRTceilf(i - 0.9f));
|
|
for (float i = -1000.0f; i <= 1000.0f; i += 1.0f) CHECK(EsCRTfloorf(i + 0.1f) == EsCRTfloorf(i + 0.9f));
|
|
for (double i = -1000.0; i <= 1000.0; i += 1.0) CHECK(EsCRTceil(i) == i);
|
|
for (double i = -1000.0; i <= 1000.0; i += 1.0) CHECK(EsCRTfloor(i) == i);
|
|
for (double i = -1000.0; i <= 1000.0; i += 1.0) CHECK(EsCRTceil(i - 0.00000000001) == i);
|
|
for (double i = -1000.0; i <= 1000.0; i += 1.0) CHECK(EsCRTfloor(i + 0.00000000001) == i);
|
|
for (double i = -1000.0; i <= 1000.0; i += 1.0) CHECK(EsCRTceil(i - 0.1) == i);
|
|
for (double i = -1000.0; i <= 1000.0; i += 1.0) CHECK(EsCRTfloor(i + 0.1) == i);
|
|
for (double i = -1000.0; i <= 1000.0; i += 1.0) CHECK(EsCRTceil(i - 0.5) == i);
|
|
for (double i = -1000.0; i <= 1000.0; i += 1.0) CHECK(EsCRTfloor(i + 0.5) == i);
|
|
for (double i = -1000.0; i <= 1000.0; i += 1.0) CHECK(EsCRTceil(i - 0.9) == i);
|
|
for (double i = -1000.0; i <= 1000.0; i += 1.0) CHECK(EsCRTfloor(i + 0.9) == i);
|
|
for (double i = -1000.0; i <= 1000.0; i += 1.0) CHECK(EsCRTceil(i - 0.1) == EsCRTceil(i - 0.9));
|
|
for (double i = -1000.0; i <= 1000.0; i += 1.0) CHECK(EsCRTfloor(i + 0.1) == EsCRTfloor(i + 0.9));
|
|
|
|
for (float x = -10.0f; x <= 10.0f; x += 0.1f) for (float y = -10.0f; y <= 10.0f; y += 0.1f) CHECK(AreFloatsRoughlyEqual(x - (int64_t) (x / y) * y, EsCRTfmodf(x, y)));
|
|
for (double x = -10.0; x <= 10.0; x += 0.1) for (double y = -10.0; y <= 10.0; y += 0.1) CHECK(AreDoublesRoughlyEqual(x - (int64_t) (x / y) * y, EsCRTfmod(x, y)));
|
|
|
|
CHECK(EsCRTisnanf(0.0f / 0.0f));
|
|
CHECK(!EsCRTisnanf(0.0f / 1.0f));
|
|
CHECK(!EsCRTisnanf(1.0f / 0.0f));
|
|
CHECK(!EsCRTisnanf(-1.0f / 0.0f));
|
|
|
|
// TODO The precision of powf is really bad!! Get it to the point where it can use AreFloatsRoughlyEqual.
|
|
for (float i = 0.0f; i <= 10.0f; i += 0.1f) CHECK(AreFloatsRoughlyEqual(EsCRTsqrtf(i), EsCRTpowf(i, 1.0f / 2.0f)));
|
|
for (float i = 0.0f; i <= 10.0f; i += 0.1f) CHECK(AreFloatsRoughlyEqual(EsCRTcbrtf(i), EsCRTpowf(i, 1.0f / 3.0f)));
|
|
for (float i = 0.0f; i <= 10.0f; i += 0.1f) CHECK(AreFloatsRoughlyEqual2(i * i, EsCRTpowf(i, 2.0f)));
|
|
for (float i = 0.1f; i <= 10.0f; i += 0.1f) CHECK(AreFloatsRoughlyEqual2(1.0f / (i * i), EsCRTpowf(i, -2.0f)));
|
|
for (double i = 0.0; i <= 10.0; i += 0.1) CHECK(AreDoublesRoughlyEqual(EsCRTsqrt(i), EsCRTpow(i, 1.0 / 2.0)));
|
|
for (double i = 0.0; i <= 10.0; i += 0.1) CHECK(AreDoublesRoughlyEqual(EsCRTcbrt(i), EsCRTpow(i, 1.0 / 3.0)));
|
|
for (double i = 0.0; i <= 10.0; i += 0.1) CHECK(AreDoublesRoughlyEqual(i * i, EsCRTpow(i, 2.0)));
|
|
for (double i = 0.1; i <= 10.0; i += 0.1) CHECK(AreDoublesRoughlyEqual(1.0 / (i * i), EsCRTpow(i, -2.0)));
|
|
|
|
CHECK(AreFloatsRoughlyEqual(EsCRTexpf(1.0f), 2.718281828459f));
|
|
for (float i = -10.0f; i <= 4.0f; i += 0.1f) CHECK(AreFloatsRoughlyEqual(EsCRTexpf(i), EsCRTpowf(2.718281828459f, i)));
|
|
CHECK(AreDoublesRoughlyEqual(EsCRTexp(1.0), 2.718281828459));
|
|
for (double i = -10.0; i <= 4.0; i += 0.1) CHECK(AreDoublesRoughlyEqual(EsCRTexp(i), EsCRTpow(2.718281828459, i)));
|
|
CHECK(AreFloatsRoughlyEqual(EsCRTexp2f(1.0f), 2.0f));
|
|
for (float i = -10.0f; i <= 4.0f; i += 0.1f) CHECK(AreFloatsRoughlyEqual(EsCRTexp2f(i), EsCRTpowf(2.0f, i)));
|
|
CHECK(AreDoublesRoughlyEqual(EsCRTexp2(1.0), 2.0));
|
|
for (double i = -10.0; i <= 4.0; i += 0.1) CHECK(AreDoublesRoughlyEqual(EsCRTexp2(i), EsCRTpow(2.0, i)));
|
|
CHECK(AreFloatsRoughlyEqual(EsCRTlog2f(2.0f), 1.0f));
|
|
for (float i = -10.0f; i <= 4.0f; i += 0.1f) CHECK(AreFloatsRoughlyEqual(EsCRTlog2f(EsCRTexp2f(i)), i));
|
|
CHECK(AreDoublesRoughlyEqual(EsCRTlog2(2.0), 1.0));
|
|
for (double i = -10.0; i <= 4.0; i += 0.1) CHECK(AreDoublesRoughlyEqual(EsCRTlog2(EsCRTexp2(i)), i));
|
|
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
bool CRTStringFunctions() {
|
|
// TODO strncmp, strncpy, strnlen, atod, atoi, atof, strtod, strtof, strtol, strtoul.
|
|
|
|
int checkIndex = 0;
|
|
|
|
for (int i = 0; i < 256; i++) CHECK(EsCRTisalpha(i) == ((i >= 'a' && i <= 'z') || (i >= 'A' && i <= 'Z')));
|
|
for (int i = 0; i < 256; i++) CHECK(EsCRTisdigit(i) == (i >= '0' && i <= '9'));
|
|
for (int i = 0; i < 256; i++) CHECK(EsCRTisupper(i) == (i >= 'A' && i <= 'Z'));
|
|
for (int i = 0; i < 256; i++) CHECK(EsCRTisxdigit(i) == ((i >= '0' && i <= '9') || (i >= 'A' && i <= 'F') || (i >= 'a' && i <= 'f')));
|
|
for (int i = 0; i <= 256; i++) CHECK((EsCRTtolower(i) != i) == (i >= 'A' && i <= 'Z'));
|
|
|
|
CHECK(0 > EsCRTstrcmp("a", "ab"));
|
|
CHECK(0 < EsCRTstrcmp("ab", "a"));
|
|
CHECK(0 > EsCRTstrcmp("ab", "ac"));
|
|
CHECK(0 > EsCRTstrcmp("ac", "bc"));
|
|
CHECK(0 > EsCRTstrcmp("", "a"));
|
|
CHECK(0 < EsCRTstrcmp("a", ""));
|
|
CHECK(0 < EsCRTstrcmp("a", "A"));
|
|
CHECK(0 == EsCRTstrcmp("", ""));
|
|
CHECK(0 == EsCRTstrcmp("a", "a"));
|
|
CHECK(0 == EsCRTstrcmp("ab", "ab"));
|
|
|
|
char x[10];
|
|
EsCRTstrcpy(x, "hello");
|
|
CHECK(0 == EsCRTstrcmp(x, "hello"));
|
|
EsCRTstrcat(x, "!");
|
|
CHECK(0 == EsCRTstrcmp(x, "hello!"));
|
|
|
|
CHECK(!EsCRTstrchr(x, '.'));
|
|
CHECK(x + 0 == EsCRTstrchr(x, 'h'));
|
|
CHECK(x + 1 == EsCRTstrchr(x, 'e'));
|
|
CHECK(x + 2 == EsCRTstrchr(x, 'l'));
|
|
CHECK(x + 4 == EsCRTstrchr(x, 'o'));
|
|
CHECK(x + 6 == EsCRTstrchr(x, 0));
|
|
CHECK(!EsCRTstrstr(x, "."));
|
|
CHECK(!EsCRTstrstr(x, "le"));
|
|
CHECK(!EsCRTstrstr(x, "oo"));
|
|
CHECK(!EsCRTstrstr(x, "ah"));
|
|
CHECK(!EsCRTstrstr(x, "lle"));
|
|
CHECK(x + 0 == EsCRTstrstr(x, ""));
|
|
CHECK(x + 0 == EsCRTstrstr(x, "h"));
|
|
CHECK(x + 0 == EsCRTstrstr(x, "he"));
|
|
CHECK(x + 0 == EsCRTstrstr(x, "hello"));
|
|
CHECK(x + 0 == EsCRTstrstr(x, "hello!"));
|
|
CHECK(x + 1 == EsCRTstrstr(x, "ell"));
|
|
CHECK(x + 1 == EsCRTstrstr(x, "ello!"));
|
|
CHECK(x + 3 == EsCRTstrstr(x, "lo"));
|
|
|
|
CHECK(0 == EsCRTstrlen(""));
|
|
CHECK(6 == EsCRTstrlen(x));
|
|
|
|
char *copy = EsCRTstrdup(x);
|
|
CHECK(0 == EsCRTstrcmp(copy, x));
|
|
EsCRTfree(copy);
|
|
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
bool CRTOtherFunctions() {
|
|
// TODO setjmp, longjmp.
|
|
// Note that malloc, free and realloc are assumed to be working if EsHeapAllocate, EsHeapFree and EsHeapReallocate are.
|
|
|
|
int checkIndex = 0;
|
|
|
|
uint8_t x[4] = { 1, 2, 3, 4 };
|
|
uint8_t y[4] = { 1, 3, 3, 4 };
|
|
uint8_t z[4] = { 6, 7, 8, 9 };
|
|
|
|
CHECK(0 > EsCRTmemcmp(x, y, 4));
|
|
CHECK(0 > EsCRTmemcmp(x, y, 3));
|
|
CHECK(0 > EsCRTmemcmp(x, y, 2));
|
|
CHECK(0 < EsCRTmemcmp(y, x, 4));
|
|
CHECK(0 < EsCRTmemcmp(y, x, 3));
|
|
CHECK(0 < EsCRTmemcmp(y, x, 2));
|
|
CHECK(0 == EsCRTmemcmp(x, y, 1));
|
|
CHECK(0 == EsCRTmemcmp(x, y, 0));
|
|
CHECK(0 == EsCRTmemcmp(y, x, 1));
|
|
CHECK(0 == EsCRTmemcmp(y, x, 0));
|
|
|
|
for (int i = 0; i < 4; i++) CHECK(x + i == EsCRTmemchr(x, i + 1, 4));
|
|
for (int i = 4; i < 8; i++) CHECK(!EsCRTmemchr(x, i + 1, 4));
|
|
|
|
y[3] = 5;
|
|
EsCRTmemcpy(x, y, 2);
|
|
CHECK(x[0] == 1 && x[1] == 3 && x[2] == 3 && x[3] == 4);
|
|
CHECK(y[0] == 1 && y[1] == 3 && y[2] == 3 && y[3] == 5);
|
|
EsCRTmemcpy(x, y, 4);
|
|
CHECK(x[0] == 1 && x[1] == 3 && x[2] == 3 && x[3] == 5);
|
|
CHECK(y[0] == 1 && y[1] == 3 && y[2] == 3 && y[3] == 5);
|
|
EsCRTmemset(x, 0xCC, 4);
|
|
for (int i = 0; i < 4; i++) CHECK(x[i] == 0xCC);
|
|
CHECK(y[0] == 1 && y[1] == 3 && y[2] == 3 && y[3] == 5);
|
|
EsCRTmemset(y, 0x00, 4);
|
|
for (int i = 0; i < 4; i++) CHECK(y[i] == 0x00);
|
|
for (int i = 0; i < 4; i++) CHECK(x[i] == 0xCC);
|
|
CHECK(z[0] == 6 && z[1] == 7 && z[2] == 8 && z[3] == 9);
|
|
|
|
y[0] = 0, y[1] = 1, y[2] = 2, y[3] = 3;
|
|
EsCRTmemmove(y + 1, y + 2, 2);
|
|
CHECK(y[0] == 0 && y[1] == 2 && y[2] == 3 && y[3] == 3);
|
|
y[0] = 0, y[1] = 1, y[2] = 2, y[3] = 3;
|
|
EsCRTmemmove(y + 2, y + 1, 2);
|
|
CHECK(y[0] == 0 && y[1] == 1 && y[2] == 1 && y[3] == 2);
|
|
|
|
for (int i = 0; i < 100000; i++) {
|
|
uint8_t *p = (uint8_t *) EsCRTcalloc(i, 1);
|
|
uint8_t *q = (uint8_t *) EsCRTmalloc(i);
|
|
if (i == 0) { CHECK(!p && !q); continue; }
|
|
CHECK(p != q);
|
|
CHECK(p && q);
|
|
for (int j = 0; j < i; j++) CHECK(!p[j]);
|
|
EsCRTfree(p);
|
|
EsCRTfree(q);
|
|
if (i > 1000) i += 100;
|
|
if (i > 10000) i += 1000;
|
|
}
|
|
|
|
uint8_t *array = (uint8_t *) EsCRTmalloc(10000);
|
|
size_t *count = (size_t *) EsCRTcalloc(256, sizeof(size_t));
|
|
EsRandomSeed(15);
|
|
array[0] = 100;
|
|
count[100]++;
|
|
for (int i = 1; i < 10000; i++) { array[i] = (EsRandomU8() & ~1) ?: 2; count[array[i]]++; }
|
|
EsCRTqsort(array, 10000, 1, CompareU8);
|
|
int p = 0;
|
|
for (uintptr_t i = 0; i < 256; i++) for (uintptr_t j = 0; j < count[i]; j++, p++) CHECK(array[p] == i);
|
|
CHECK(p == 10000);
|
|
uint8_t n = 100;
|
|
uint8_t *q = (uint8_t *) EsCRTbsearch(&n, array, 10000, 1, CompareU8);
|
|
CHECK(q && q[0] == 100);
|
|
n = 0;
|
|
CHECK(!EsCRTbsearch(&n, array, 10000, 1, CompareU8));
|
|
n = 255;
|
|
CHECK(!EsCRTbsearch(&n, array, 10000, 1, CompareU8));
|
|
|
|
for (n = 0; n < 255; n++) {
|
|
q = (uint8_t *) EsCRTbsearch(&n, array, 10000, 1, CompareU8);
|
|
if (count[n]) CHECK(q && *q == n);
|
|
else CHECK(!q);
|
|
}
|
|
|
|
EsCRTfree(array);
|
|
EsCRTfree(count);
|
|
|
|
#ifdef ES_BITS_32
|
|
CHECK(!EsCRTcalloc(0x7FFFFFFF, 0x7FFFFFFF));
|
|
CHECK(!EsCRTcalloc(0xFFFFFFFF, 0xFFFFFFFF));
|
|
#endif
|
|
#ifdef ES_BITS_64
|
|
CHECK(!EsCRTcalloc(0x7FFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFFF));
|
|
CHECK(!EsCRTcalloc(0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF));
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
bool PerformanceTimerDrift() {
|
|
int checkIndex = 0;
|
|
|
|
EsDateComponents start, end;
|
|
|
|
EsDateNowUTC(&start);
|
|
EsPerformanceTimerPush();
|
|
|
|
for (uintptr_t i = 0; i < 50000000; i++) {
|
|
EsCStringLength("Test");
|
|
}
|
|
|
|
double performanceTime = EsPerformanceTimerPop();
|
|
EsDateNowUTC(&end);
|
|
double mainTime = (DateToLinear(&end) - DateToLinear(&start)) / 1000.0;
|
|
|
|
EsPrint("Performance timer: %F s.\n", performanceTime);
|
|
EsPrint("Main timer: %F s.\n", mainTime);
|
|
|
|
// TODO Improve the quality of the performance timer, if better timer sources are available, like the HPET.
|
|
CHECK(EsCRTfabs(performanceTime - mainTime) / mainTime < 0.2); // Less than a 20% drift.
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
EsTextbox *textbox;
|
|
|
|
Array<char> master;
|
|
Array<Array<char>> undo;
|
|
|
|
void OffsetToLineAndByte(uintptr_t offset, int32_t *_line, int32_t *_byte) {
|
|
int32_t line = 0, byte = 0;
|
|
|
|
for (uintptr_t i = 0; i < offset; i++) {
|
|
if (master[i] == '\n') {
|
|
line++;
|
|
byte = 0;
|
|
} else {
|
|
byte++;
|
|
}
|
|
}
|
|
|
|
*_line = line;
|
|
*_byte = byte;
|
|
}
|
|
|
|
bool Compare() {
|
|
size_t bytes;
|
|
char *contents = EsTextboxGetContents(textbox, &bytes);
|
|
// EsPrint("\tContents: '%e'\n\tMaster: '%e'\n", bytes, contents, arrlenu(master), master);
|
|
if (bytes != master.Length()) return false;
|
|
if (EsMemoryCompare(&master[0], contents, bytes)) return false;
|
|
EsHeapFree(contents);
|
|
return true;
|
|
}
|
|
|
|
void FakeUndoItem(const void *, EsUndoManager *manager, EsMessage *message) {
|
|
if (message->type == ES_MSG_UNDO_INVOKE) {
|
|
EsUndoPush(manager, FakeUndoItem, nullptr, 0);
|
|
}
|
|
}
|
|
|
|
void AddUndoItem() {
|
|
Array<char> copy = {};
|
|
copy.SetLength(master.Length());
|
|
if (copy.Length()) EsMemoryCopy(©[0], &master[0], copy.Length());
|
|
undo.Add(copy);
|
|
}
|
|
|
|
void Complete() {
|
|
EsUndoPush(textbox->instance->undoManager, FakeUndoItem, nullptr, 0);
|
|
EsUndoEndGroup(textbox->instance->undoManager);
|
|
}
|
|
|
|
bool Insert(uintptr_t offset, const char *string, size_t stringBytes) {
|
|
if (!stringBytes) return true;
|
|
AddUndoItem();
|
|
// EsPrint("Insert '%e' at %d.\n", stringBytes, string, offset);
|
|
int32_t line, byte;
|
|
OffsetToLineAndByte(offset, &line, &byte);
|
|
EsTextboxSetSelection(textbox, line, byte, line, byte);
|
|
EsTextboxInsert(textbox, string, stringBytes);
|
|
master.InsertMany(offset, stringBytes);
|
|
EsMemoryCopy(&master[offset], string, stringBytes);
|
|
if (!Compare()) return false;
|
|
Complete();
|
|
return true;
|
|
}
|
|
|
|
bool Delete(uintptr_t from, uintptr_t to) {
|
|
if (from == to) return true;
|
|
AddUndoItem();
|
|
// EsPrint("Delete from %d to %d.\n", from, to);
|
|
int32_t fromLine, fromByte, toLine, toByte;
|
|
OffsetToLineAndByte(from, &fromLine, &fromByte);
|
|
OffsetToLineAndByte(to, &toLine, &toByte);
|
|
EsTextboxSetSelection(textbox, fromLine, fromByte, toLine, toByte);
|
|
EsTextboxInsert(textbox, 0, 0);
|
|
if (to > from) master.DeleteMany(from, to - from);
|
|
else master.DeleteMany(to, from - to);
|
|
if (!Compare()) return false;
|
|
Complete();
|
|
return true;
|
|
}
|
|
|
|
bool TextboxEditOperations() {
|
|
int checkIndex = 0;
|
|
textbox = EsTextboxCreate(EsWindowCreate(_EsInstanceCreate(sizeof(EsInstance), nullptr), ES_WINDOW_PLAIN), ES_TEXTBOX_ALLOW_TABS | ES_TEXTBOX_MULTILINE);
|
|
EsRandomSeed(10);
|
|
EsTextboxSetUndoManager(textbox, textbox->instance->undoManager);
|
|
|
|
CHECK(Insert(0, EsLiteral("hello\n\t\t\ttest\n\t\t\tworld")));
|
|
CHECK(Insert(13, EsLiteral("\n")));
|
|
CHECK(Insert(14, EsLiteral("\t\t\t")));
|
|
CHECK(Insert(26, EsLiteral("\n")));
|
|
CHECK(Insert(27, EsLiteral("\t\t\t")));
|
|
CHECK(Insert(30, EsLiteral("\n")));
|
|
CHECK(Insert(31, EsLiteral("\t\t\t")));
|
|
|
|
char *initialText = (char *) EsHeapAllocate(100000, false);
|
|
for (uintptr_t i = 0; i < 100000; i++) initialText[i] = EsRandomU8() < 0x40 ? '\n' : ((EsRandomU8() % 26) + 'a');
|
|
CHECK(Insert(0, initialText, 100000));
|
|
EsHeapFree(initialText);
|
|
|
|
for (uintptr_t i = 0; i < 10000; i++) {
|
|
uint8_t action = EsRandomU8();
|
|
|
|
if (action < 0x70) {
|
|
size_t stringBytes = EsRandomU8() & 0x1F;
|
|
char string[0x20];
|
|
|
|
for (uintptr_t i = 0; i < stringBytes; i++) {
|
|
string[i] = EsRandomU8() < 0x40 ? '\n' : ((EsRandomU8() % 26) + 'a');
|
|
}
|
|
|
|
CHECK(Insert(EsRandomU64() % (master.Length() + 1), string, stringBytes));
|
|
} else if (action < 0xE0) {
|
|
if (master.Length()) {
|
|
CHECK(Delete(EsRandomU64() % master.Length(), EsRandomU64() % master.Length()));
|
|
}
|
|
} else {
|
|
if (!EsUndoIsEmpty(textbox->instance->undoManager, false)) {
|
|
// EsPrint("Undo.\n");
|
|
EsUndoInvokeGroup(textbox->instance->undoManager, false);
|
|
master.Free();
|
|
master = undo.Last();
|
|
undo.Pop();
|
|
CHECK(Compare());
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
struct {
|
|
int a, b;
|
|
} testStruct = {
|
|
.a = 1, .b = 2,
|
|
};
|
|
|
|
const int testVariable = 3;
|
|
|
|
bool DirectoryEnumerateRecursive(const char *path, size_t pathBytes) {
|
|
size_t count;
|
|
EsError error;
|
|
EsDirectoryChild *buffer = EsDirectoryEnumerate(path, pathBytes, &count, &error);
|
|
|
|
if (error != ES_SUCCESS) {
|
|
EsPrint("Error %i enumerating at path \"%s\".\n", error, pathBytes, path);
|
|
return false;
|
|
}
|
|
|
|
for (uintptr_t i = 0; i < count; i++) {
|
|
char *childPath = (char *) EsHeapAllocate(pathBytes + 1 + buffer[i].nameBytes, false);
|
|
size_t childPathBytes = EsStringFormat(childPath, ES_STRING_FORMAT_ENOUGH_SPACE, "%s/%s", pathBytes, path, buffer[i].nameBytes, buffer[i].name);
|
|
|
|
if (buffer[i].type == ES_NODE_FILE) {
|
|
size_t dataBytes;
|
|
EsError error;
|
|
void *data = EsFileReadAll(childPath, childPathBytes, &dataBytes, &error);
|
|
|
|
if (error != ES_SUCCESS) {
|
|
EsPrint("Error %i reading path \"%s\".\n", (EsError) count, childPathBytes, childPath);
|
|
return false;
|
|
}
|
|
|
|
if (dataBytes != (size_t) buffer[i].fileSize) {
|
|
EsPrint("File size mismatch reading path \"%s\" (got %d from EsFileReadAll, got %d from EsDirectoryEnumerate).\n",
|
|
childPathBytes, childPath, dataBytes, buffer[i].fileSize);
|
|
return false;
|
|
}
|
|
|
|
EsHeapFree(data);
|
|
} else if (buffer[i].type == ES_NODE_DIRECTORY) {
|
|
if (!DirectoryEnumerateRecursive(childPath, childPathBytes)) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
EsHeapFree(buffer);
|
|
return true;
|
|
}
|
|
|
|
bool OldTests2018() {
|
|
int checkIndex = 0;
|
|
|
|
CHECK(testStruct.a == 1);
|
|
CHECK(testStruct.b == 2);
|
|
CHECK(testVariable == 3);
|
|
testStruct.a += 3;
|
|
CHECK(testStruct.a == 4);
|
|
CHECK(testStruct.b == 2);
|
|
CHECK(testVariable == 3);
|
|
|
|
CHECK(DirectoryEnumerateRecursive(EsLiteral("|Settings:")));
|
|
|
|
for (int count = 16; count < 100; count += 30) {
|
|
EsHandle handles[100];
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
char buffer[256];
|
|
size_t length = EsStringFormat(buffer, 256, "|Settings:/TestFolder/%d", i);
|
|
EsFileInformation node = EsFileOpen(buffer, length, ES_NODE_CREATE_DIRECTORIES | ES_NODE_FAIL_IF_FOUND | ES_FILE_WRITE);
|
|
CHECK(node.error == ES_SUCCESS);
|
|
handles[i] = node.handle;
|
|
}
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
CHECK(ES_SUCCESS == EsFileDelete(handles[i]));
|
|
}
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
EsHandleClose(handles[i]);
|
|
}
|
|
}
|
|
|
|
{
|
|
EsFileWriteAll(EsLiteral("|Settings:/TestFolder/a.txt"), EsLiteral("hello"));
|
|
EsFileWriteAll(EsLiteral("|Settings:/b.txt"), EsLiteral("world"));
|
|
CHECK(EsPathExists(EsLiteral("|Settings:/TestFolder/a.txt")));
|
|
CHECK(EsPathExists(EsLiteral("|Settings:/b.txt")));
|
|
CHECK(!EsPathExists(EsLiteral("|Settings:/TestFolder/b.txt")));
|
|
CHECK(!EsPathExists(EsLiteral("|Settings:/a.txt")));
|
|
CHECK(ES_SUCCESS == EsPathMove(EsLiteral("|Settings:/TestFolder/a.txt"), EsLiteral("|Settings:/a.txt"), ES_FLAGS_DEFAULT));
|
|
CHECK(!EsPathExists(EsLiteral("|Settings:/TestFolder/a.txt")));
|
|
CHECK(EsPathExists(EsLiteral("|Settings:/b.txt")));
|
|
CHECK(!EsPathExists(EsLiteral("|Settings:/TestFolder/b.txt")));
|
|
CHECK(EsPathExists(EsLiteral("|Settings:/a.txt")));
|
|
CHECK(ES_ERROR_FILE_DOES_NOT_EXIST == EsPathMove(EsLiteral("|Settings:/TestFolder/a.txt"), EsLiteral("|Settings:/a.txt"), ES_FLAGS_DEFAULT));
|
|
CHECK(ES_ERROR_ALREADY_EXISTS == EsPathMove(EsLiteral("|Settings:/a.txt"), EsLiteral("|Settings:/b.txt"), ES_FLAGS_DEFAULT));
|
|
CHECK(ES_ERROR_ALREADY_EXISTS == EsPathMove(EsLiteral("|Settings:/a.txt"), EsLiteral("|Settings:/a.txt"), ES_FLAGS_DEFAULT));
|
|
CHECK(ES_ERROR_TARGET_WITHIN_SOURCE == EsPathMove(EsLiteral("|Settings:/"), EsLiteral("|Settings:/TestFolder/TargetWithinSource"), ES_FLAGS_DEFAULT));
|
|
CHECK(!EsPathExists(EsLiteral("|Settings:/TestFolder/a.txt")));
|
|
CHECK(EsPathExists(EsLiteral("|Settings:/b.txt")));
|
|
CHECK(!EsPathExists(EsLiteral("|Settings:/TestFolder/b.txt")));
|
|
CHECK(EsPathExists(EsLiteral("|Settings:/a.txt")));
|
|
}
|
|
|
|
CHECK(DirectoryEnumerateRecursive(EsLiteral("|Settings:")));
|
|
|
|
{
|
|
void *a = EsCRTmalloc(0x100000);
|
|
CHECK(a);
|
|
void *b = EsCRTrealloc(a, 0x1000);
|
|
CHECK(b);
|
|
void *c = EsCRTrealloc(b, 0x100000);
|
|
CHECK(c);
|
|
EsCRTfree(c);
|
|
}
|
|
|
|
{
|
|
char b[] = "abcdef";
|
|
CHECK(EsCRTstrlen(b) == 6);
|
|
CHECK(EsCRTstrnlen(b, 3) == 3);
|
|
CHECK(EsCRTstrnlen(b, 10) == 6);
|
|
}
|
|
|
|
{
|
|
CHECK(EsCRTstrtol("\n\f\n -0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaAAAAAAAaaaaaa", nullptr, 0) == LONG_MIN);
|
|
char *x = (char *) "+03", *y;
|
|
CHECK(EsCRTstrtol(x, &y, 4) == 3 && y == x + 3);
|
|
}
|
|
|
|
{
|
|
EsFileInformation node = EsFileOpen(EsLiteral("|Settings:/ResizeFileTest.txt"), ES_FILE_WRITE | ES_NODE_FAIL_IF_FOUND);
|
|
CHECK(node.error == ES_SUCCESS);
|
|
|
|
// TODO Failing large file resizes.
|
|
#if 0
|
|
EsFileResize(node.handle, (uint64_t) 0xFFFFFFFFFFFF);
|
|
#endif
|
|
|
|
uint8_t buffer[512];
|
|
|
|
for (uintptr_t i = 1; i < 128; i++) {
|
|
for (uintptr_t j = 0; j < 512; j++) {
|
|
buffer[j] = i;
|
|
}
|
|
|
|
// OSPrint("Resizing file to %d\n", i * 512);
|
|
EsFileResize(node.handle, i * 512);
|
|
// OSPrint("Write to %d\n", (i - 1) * 512);
|
|
EsFileWriteSync(node.handle, (i - 1) * 512, 512, buffer);
|
|
}
|
|
|
|
for (uintptr_t i = 1; i < 128; i++) {
|
|
// OSPrint("Read from %d\n", (i - 1) * 512);
|
|
EsFileReadSync(node.handle, (i - 1) * 512, 512, buffer);
|
|
|
|
for (uintptr_t j = 0; j < 512; j++) {
|
|
CHECK(buffer[j] == i);
|
|
}
|
|
}
|
|
|
|
for (uintptr_t i = 126; i > 0; i--) {
|
|
// OSPrint("Resizing file to %d\n", i * 512);
|
|
EsFileResize(node.handle, i * 512);
|
|
}
|
|
|
|
for (uintptr_t i = 1; i < 2; i++) {
|
|
// OSPrint("Read from %d\n", (i - 1) * 512);
|
|
EsFileReadSync(node.handle, (i - 1) * 512, 512, buffer);
|
|
|
|
for (uintptr_t j = 0; j < 512; j++) {
|
|
CHECK(buffer[j] == i);
|
|
}
|
|
}
|
|
|
|
EsHandleClose(node.handle);
|
|
}
|
|
|
|
{
|
|
EsFileInformation node = EsFileOpen(EsLiteral("|Settings:/MapFile.txt"), ES_FILE_WRITE_SHARED);
|
|
CHECK(node.error == ES_SUCCESS);
|
|
EsFileResize(node.handle, 1048576);
|
|
uint32_t *buffer = (uint32_t *) EsHeapAllocate(1048576, false);
|
|
for (int i = 0; i < 262144; i++) buffer[i] = i;
|
|
EsFileWriteSync(node.handle, 0, 1048576, buffer);
|
|
EsFileReadSync(node.handle, 0, 1048576, buffer);
|
|
for (uintptr_t i = 0; i < 262144; i++) CHECK(buffer[i] == i);
|
|
EsFileInformation node2 = EsFileOpen(EsLiteral("|Settings:/MapFile.txt"), ES_FILE_READ_SHARED);
|
|
CHECK(node.error == ES_SUCCESS);
|
|
uint32_t *pointer = (uint32_t *) EsMemoryMapObject(node2.handle, 0, ES_MEMORY_MAP_OBJECT_ALL, ES_MEMORY_MAP_OBJECT_READ_ONLY);
|
|
CHECK(pointer);
|
|
uint32_t *pointer2 = (uint32_t *) EsMemoryMapObject(node2.handle, 0, ES_MEMORY_MAP_OBJECT_ALL, ES_MEMORY_MAP_OBJECT_READ_ONLY);
|
|
CHECK(pointer2);
|
|
for (uintptr_t i = 4096; i < 262144; i++) CHECK(pointer[i] == buffer[i]);
|
|
for (int i = 0; i < 262144; i++) buffer[i] = i + 100;
|
|
EsFileWriteSync(node.handle, 0, 1048576, buffer);
|
|
EsFileReadSync(node.handle, 0, 1048576, buffer);
|
|
for (uintptr_t i = 0; i < 262144; i++) CHECK(buffer[i] == i + 100);
|
|
for (uintptr_t i = 4096; i < 262144; i++) CHECK(pointer[i] == buffer[i]);
|
|
for (uintptr_t i = 4096; i < 262144; i++) CHECK(pointer2[i] == buffer[i]);
|
|
EsMemoryUnreserve(pointer);
|
|
EsHandleClose(node.handle);
|
|
EsHandleClose(node2.handle);
|
|
EsMemoryUnreserve(pointer2);
|
|
}
|
|
|
|
{
|
|
const char *path = "|Settings:/OS/new_dir/test2.txt";
|
|
EsFileInformation node = EsFileOpen(path, EsCStringLength(path), ES_FILE_WRITE | ES_NODE_CREATE_DIRECTORIES);
|
|
CHECK(node.error == ES_SUCCESS);
|
|
CHECK(ES_SUCCESS == EsFileResize(node.handle, 8));
|
|
char buffer[8];
|
|
buffer[0] = 'a';
|
|
buffer[1] = 'b';
|
|
EsFileWriteSync(node.handle, 0, 1, buffer);
|
|
buffer[0] = 'b';
|
|
buffer[1] = 'c';
|
|
size_t bytesRead = EsFileReadSync(node.handle, 0, 8, buffer);
|
|
CHECK(bytesRead == 8);
|
|
CHECK(buffer[0] == 'a' && buffer[1] == 0 && buffer[2] == 0);
|
|
CHECK(EsFileGetSize(node.handle) == 8);
|
|
EsHandleClose(node.handle);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
bool HeapReallocate() {
|
|
void *a = EsHeapReallocate(nullptr, 128, true);
|
|
EsHeapValidate();
|
|
a = EsHeapReallocate(a, 256, true);
|
|
EsHeapValidate();
|
|
a = EsHeapReallocate(a, 128, true);
|
|
EsHeapValidate();
|
|
a = EsHeapReallocate(a, 65536, true);
|
|
EsHeapValidate();
|
|
a = EsHeapReallocate(a, 128, true);
|
|
EsHeapValidate();
|
|
a = EsHeapReallocate(a, 128, true);
|
|
EsHeapValidate();
|
|
void *b = EsHeapReallocate(nullptr, 64, true);
|
|
EsHeapValidate();
|
|
void *c = EsHeapReallocate(nullptr, 64, true);
|
|
EsHeapValidate();
|
|
EsHeapReallocate(b, 0, true);
|
|
EsHeapValidate();
|
|
a = EsHeapReallocate(a, 128 + 88, true);
|
|
EsHeapValidate();
|
|
a = EsHeapReallocate(a, 128, true);
|
|
EsHeapValidate();
|
|
EsHeapReallocate(a, 0, true);
|
|
EsHeapValidate();
|
|
EsHeapReallocate(c, 0, true);
|
|
EsHeapValidate();
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
bool ArenaRandomAllocations() {
|
|
int checkIndex = 0;
|
|
Arena arena = {};
|
|
ArenaInitialise(&arena, 3, sizeof(int));
|
|
Array<void *> allocations = {};
|
|
EsRandomSeed(20);
|
|
|
|
for (uintptr_t i = 0; i < 500000; i++) {
|
|
if ((EsRandomU8() & 1) || !allocations.Length()) {
|
|
void *allocation = ArenaAllocate(&arena, false);
|
|
for (uintptr_t i = 0; i < allocations.Length(); i++) CHECK(allocations[i] != allocation);
|
|
allocations.Add(allocation);
|
|
} else {
|
|
int index = EsRandomU64() % allocations.Length();
|
|
ArenaFree(&arena, allocations[index]);
|
|
allocations.DeleteSwap(index);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
bool rangeSetCheck[1000];
|
|
RangeSet rangeSet = {};
|
|
|
|
bool RangeSetModify(bool set, int x, int y) {
|
|
for (int i = x; i < y; i++) {
|
|
rangeSetCheck[i] = set;
|
|
}
|
|
|
|
if (set) {
|
|
if (!rangeSet.Set(x, y, nullptr, true)) {
|
|
return false;
|
|
}
|
|
} else {
|
|
if (!rangeSet.Clear(x, y, nullptr, true)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
for (uintptr_t i = 0; i < sizeof(rangeSetCheck); i++) {
|
|
if (rangeSetCheck[i]) {
|
|
if (!rangeSet.Find(i, false)) {
|
|
return false;
|
|
}
|
|
} else {
|
|
if (rangeSet.Find(i, false)) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool RangeSetTests() {
|
|
int checkIndex = 0;
|
|
|
|
CHECK(RangeSetModify(true, 2, 3));
|
|
CHECK(RangeSetModify(true, 4, 5));
|
|
CHECK(RangeSetModify(true, 0, 1));
|
|
CHECK(RangeSetModify(true, 1, 2));
|
|
CHECK(RangeSetModify(true, 3, 4));
|
|
CHECK(RangeSetModify(true, 10, 15));
|
|
CHECK(RangeSetModify(true, 4, 10));
|
|
CHECK(RangeSetModify(true, 20, 30));
|
|
CHECK(RangeSetModify(true, 15, 21));
|
|
CHECK(RangeSetModify(true, 50, 55));
|
|
CHECK(RangeSetModify(true, 60, 65));
|
|
CHECK(RangeSetModify(true, 40, 70));
|
|
CHECK(RangeSetModify(true, 0, 100));
|
|
|
|
CHECK(RangeSetModify(false, 50, 60));
|
|
CHECK(RangeSetModify(false, 55, 56));
|
|
CHECK(RangeSetModify(false, 50, 55));
|
|
CHECK(RangeSetModify(false, 55, 60));
|
|
CHECK(RangeSetModify(false, 50, 60));
|
|
CHECK(RangeSetModify(false, 49, 60));
|
|
CHECK(RangeSetModify(false, 49, 61));
|
|
|
|
CHECK(RangeSetModify(true, 50, 51));
|
|
CHECK(RangeSetModify(false, 48, 62));
|
|
CHECK(RangeSetModify(true, 50, 51));
|
|
CHECK(RangeSetModify(false, 48, 62));
|
|
CHECK(RangeSetModify(true, 50, 51));
|
|
CHECK(RangeSetModify(true, 52, 53));
|
|
CHECK(RangeSetModify(false, 48, 62));
|
|
CHECK(RangeSetModify(true, 50, 51));
|
|
CHECK(RangeSetModify(true, 52, 53));
|
|
CHECK(RangeSetModify(false, 47, 62));
|
|
CHECK(RangeSetModify(true, 50, 51));
|
|
CHECK(RangeSetModify(true, 52, 53));
|
|
CHECK(RangeSetModify(false, 47, 63));
|
|
CHECK(RangeSetModify(true, 50, 51));
|
|
CHECK(RangeSetModify(true, 52, 53));
|
|
CHECK(RangeSetModify(false, 46, 64));
|
|
|
|
EsRandomSeed(20);
|
|
|
|
for (uintptr_t i = 0; i < 100000; i++) {
|
|
int a = EsRandomU64() % 1000, b = EsRandomU64() % 1000;
|
|
if (b <= a) continue;
|
|
CHECK(RangeSetModify(EsRandomU8() & 1, a, b));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
bool UTF8Tests() {
|
|
int checkIndex = 0;
|
|
|
|
// Strings taken from https://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt under CC BY 4.0.
|
|
|
|
const char *goodStrings[] = {
|
|
"\xCE\xBA\xE1\xBD\xB9\xCF\x83\xCE\xBC\xCE\xB5",
|
|
"\x01",
|
|
"\xC2\x80",
|
|
"\xE0\xA0\x80",
|
|
"\xF0\x90\x80\x80",
|
|
"\x7F",
|
|
"\xDF\xBF",
|
|
"\xEF\xBF\xBF",
|
|
"\xF7\xBF\xBF\xBF",
|
|
"\xED\x9F\xBF",
|
|
"\xEE\x80\x80",
|
|
"\xEF\xBF\xBD",
|
|
"\xF4\x8F\xBF\xBF",
|
|
"\xF4\x90\x80\x80",
|
|
|
|
// Overlong sequences for non-ASCII characters are allowed.
|
|
"\xE0\x9F\xBF",
|
|
"\xF0\x8F\xBF\xBF",
|
|
|
|
// Surrogate characters are allowed (for compatability with things like NTFS).
|
|
"\xED\xA0\x80",
|
|
"\xED\xAD\xBF",
|
|
"\xED\xAE\x80",
|
|
"\xED\xAF\xBF",
|
|
"\xED\xB0\x80",
|
|
"\xED\xBE\x80",
|
|
"\xED\xBF\xBF",
|
|
"\xED\xA0\x80\xED\xB0\x80",
|
|
"\xED\xA0\x80\xED\xBF\xBF",
|
|
"\xED\xAD\xBF\xED\xB0\x80",
|
|
"\xED\xAD\xBF\xED\xBF\xBF",
|
|
"\xED\xAE\x80\xED\xB0\x80",
|
|
"\xED\xAE\x80\xED\xBF\xBF",
|
|
"\xED\xAF\xBF\xED\xB0\x80",
|
|
"\xED\xAF\xBF\xED\xBF\xBF",
|
|
};
|
|
|
|
const char *badStrings[] = {
|
|
// We don't support 5 and 6 byte characters, as they shouldn't appear in Unicode text.
|
|
"\xF8\x88\x80\x80\x80",
|
|
"\xFC\x84\x80\x80\x80\x80",
|
|
"\xFB\xBF\xBF\xBF\xBF",
|
|
"\xFD\xBF\xBF\xBF\xBF\xBF",
|
|
|
|
"\x80",
|
|
"\xBF",
|
|
"\x80\xBF",
|
|
"\x80\xBF\x80",
|
|
"\x80\xBF\x80\xBF",
|
|
"\x80\xBF\x80\xBF\x80",
|
|
"\x80\xBF\x80\xBF\x80\xBF\x80",
|
|
"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96"
|
|
"\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB"
|
|
"\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF",
|
|
"\xC0\x20\xC1\x20\xC2\x20\xC3\x20\xC4\x20\xC5\x20\xC6\x20\xC7\x20\xC8\x20\xC9\x20\xCA\x20\xCB"
|
|
"\x20\xCC\x20\xCD\x20\xCE\x20\xCF\x20\xD0\x20\xD1\x20\xD2\x20\xD3\x20\xD4\x20\xD5\x20"
|
|
"\xD6\x20\xD7\x20\xD8\x20\xD9\x20\xDA\x20\xDB\x20\xDC\x20\xDD\x20\xDE\x20\xDF\x20",
|
|
"\xE0\x20\xE1\x20\xE2\x20\xE3\x20\xE4\x20\xE5\x20\xE6\x20\xE7\x20\xE8\x20\xE9\x20\xEA\x20\xEB"
|
|
"\x20\xEC\x20\xED\x20\xEE\x20\xEF\x20",
|
|
"\xF0\x20\xF1\x20\xF2\x20\xF3\x20\xF4\x20\xF5\x20\xF6\x20\xF7\x20",
|
|
"\xF8\x20\xF9\x20\xFA\x20\xFB\x20",
|
|
"\xFC\x20\xFD\x20",
|
|
"\xC0",
|
|
"\xE0\x80",
|
|
"\xF0\x80\x80",
|
|
"\xF8\x80\x80\x80",
|
|
"\xFC\x80\x80\x80\x80",
|
|
"\xDF",
|
|
"\xEF\xBF",
|
|
"\xF7\xBF\xBF",
|
|
"\xFB\xBF\xBF\xBF",
|
|
"\xFD\xBF\xBF\xBF\xBF",
|
|
"\xC0\xE0\x80\xF0\x80\x80\xF8\x80\x80\x80\xFC\x80\x80\x80\x80\xDF\xEF\xBF\xF7\xBF\xBF\xFB\xBF"
|
|
"\xBF\xBF\xFD\xBF\xBF\xBF\xBF",
|
|
"\xFE",
|
|
"\xFF",
|
|
"\xFE\xFE\xFF\xFF",
|
|
"\xC0\xAF",
|
|
"\xE0\x80\xAF",
|
|
"\xF0\x80\x80\xAF",
|
|
"\xF8\x80\x80\x80\xAF",
|
|
"\xFC\x80\x80\x80\x80\xAF",
|
|
"\xC1\xBF",
|
|
"\xC0\x80",
|
|
"\xE0\x80\x80",
|
|
"\xF0\x80\x80\x80",
|
|
"\xF8\x80\x80\x80\x80",
|
|
"\xFC\x80\x80\x80\x80\x80",
|
|
};
|
|
|
|
for (uintptr_t i = 0; i < sizeof(goodStrings) / sizeof(goodStrings[0]); i++) {
|
|
CHECK(EsUTF8IsValid(goodStrings[i], -1));
|
|
|
|
const char *position = goodStrings[i];
|
|
|
|
while (*position) {
|
|
CHECK(utf8_value(position));
|
|
position = utf8_advance(position);
|
|
CHECK(position);
|
|
}
|
|
|
|
while (position != goodStrings[i]) {
|
|
position = utf8_retreat(position);
|
|
CHECK(position);
|
|
}
|
|
}
|
|
|
|
for (uintptr_t i = 0; i < sizeof(badStrings) / sizeof(badStrings[0]); i++) {
|
|
CHECK(!EsUTF8IsValid(badStrings[i], -1));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
EsHandle pipeRead, pipeWrite;
|
|
|
|
void PipeTestsThread2(EsGeneric) {
|
|
for (uint16_t i = 0; i < 1000; i++) {
|
|
EsPipeWrite(pipeWrite, &i, sizeof(i));
|
|
}
|
|
|
|
uint16_t *buffer = (uint16_t *) EsHeapAllocate(10000, false);
|
|
|
|
for (uint16_t i = 0; i < 1000; i++) {
|
|
for (uintptr_t i = 0; i < 5000; i++) buffer[i] = i;
|
|
EsPipeWrite(pipeWrite, buffer, 10000);
|
|
}
|
|
|
|
uint16_t s = 0x1234;
|
|
EsPipeWrite(pipeWrite, &s, sizeof(s));
|
|
EsSleep(2000);
|
|
s = 0xFEDC;
|
|
EsPipeWrite(pipeWrite, &s, sizeof(s));
|
|
EsHandleClose(pipeWrite);
|
|
|
|
EsHeapFree(buffer);
|
|
}
|
|
|
|
void PipeTestsThread3(EsGeneric) {
|
|
uint8_t data[200];
|
|
EsPipeRead(pipeRead, data, sizeof(data), false);
|
|
EsHandleClose(pipeRead);
|
|
}
|
|
|
|
bool PipeTests() {
|
|
EsPipeCreate(&pipeRead, &pipeWrite);
|
|
|
|
int checkIndex = 0;
|
|
EsThreadInformation information;
|
|
CHECK(EsThreadCreate(PipeTestsThread2, &information, nullptr) == ES_SUCCESS);
|
|
EsHandleClose(information.handle);
|
|
|
|
for (uint16_t i = 0; i < 1000; i++) {
|
|
uint16_t j;
|
|
CHECK(sizeof(j) == EsPipeRead(pipeRead, &j, sizeof(j), true));
|
|
CHECK(i == j);
|
|
}
|
|
|
|
uint16_t *buffer = (uint16_t *) EsHeapAllocate(10000, false);
|
|
|
|
for (uint16_t i = 0; i < 1000; i++) {
|
|
EsMemoryZero(buffer, 10000);
|
|
uintptr_t position = 0;
|
|
|
|
while (position < 10000) {
|
|
size_t read = EsPipeRead(pipeRead, (uint8_t *) buffer + position, 10000 - position, i >= 500);
|
|
if (i < 500) CHECK(read == 10000);
|
|
CHECK(read);
|
|
position += read;
|
|
}
|
|
|
|
CHECK(position == 10000);
|
|
|
|
for (uintptr_t i = 0; i < 5000; i++) CHECK(buffer[i] == i);
|
|
}
|
|
|
|
EsSleep(1000);
|
|
|
|
uint32_t s = 0x5678ABCD;
|
|
CHECK(2 == EsPipeRead(pipeRead, &s, sizeof(s), true));
|
|
CHECK(s == 0x56781234); // TODO Big endian support.
|
|
s = 0x5678ABCD;
|
|
CHECK(2 == EsPipeRead(pipeRead, &s, sizeof(s), false));
|
|
CHECK(s == 0x5678FEDC); // TODO Big endian support.
|
|
CHECK(0 == EsPipeRead(pipeRead, &s, sizeof(s), false));
|
|
|
|
EsHandleClose(pipeRead);
|
|
|
|
EsPipeCreate(&pipeRead, &pipeWrite);
|
|
CHECK(EsThreadCreate(PipeTestsThread3, &information, nullptr) == ES_SUCCESS);
|
|
EsHandleClose(information.handle);
|
|
size_t written = EsPipeWrite(pipeWrite, buffer, 10000);
|
|
CHECK(written > 0 && written < 10000); // The actual amountn written depends on the size of the internal pipe buffer, and whether the read happens in time.
|
|
CHECK(0 == EsPipeWrite(pipeWrite, buffer, 10000));
|
|
EsHandleClose(pipeWrite);
|
|
|
|
EsHeapFree(buffer);
|
|
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
#include <bits/syscall.h>
|
|
|
|
#define _exit(x) EsPOSIXSystemCall(SYS_exit_group, (intptr_t) x, 0, 0, 0, 0, 0)
|
|
#define close(x) EsPOSIXSystemCall(SYS_close, (intptr_t) x, 0, 0, 0, 0, 0)
|
|
#define dup2(x, y) EsPOSIXSystemCall(SYS_dup2, (intptr_t) x, (intptr_t) y, 0, 0, 0, 0)
|
|
#define execve(x, y, z) EsPOSIXSystemCall(SYS_execve, (intptr_t) x, (intptr_t) y, (intptr_t) z, 0, 0, 0)
|
|
#define exit(x) EsPOSIXSystemCall(SYS_exit_group, (intptr_t) x, 0, 0, 0, 0, 0)
|
|
#define pipe(x) EsPOSIXSystemCall(SYS_pipe, (intptr_t) x, 0, 0, 0, 0, 0)
|
|
#define read(x, y, z) EsPOSIXSystemCall(SYS_read, (intptr_t) x, (intptr_t) y, (intptr_t) z, 0, 0, 0)
|
|
#define rename(x, y) EsPOSIXSystemCall(SYS_rename, (intptr_t) x, (intptr_t) y, 0, 0, 0, 0)
|
|
#define truncate(x, y) EsPOSIXSystemCall(SYS_truncate, (intptr_t) x, (intptr_t) y, 0, 0, 0, 0)
|
|
#define unlink(x) EsPOSIXSystemCall(SYS_unlink, (intptr_t) x, 0, 0, 0, 0, 0)
|
|
#define vfork() EsPOSIXSystemCall(SYS_vfork, 0, 0, 0, 0, 0, 0)
|
|
#define wait4(x, y, z, w) EsPOSIXSystemCall(SYS_wait4, (intptr_t) x, (intptr_t) y, (intptr_t) z, (intptr_t) w, 0, 0)
|
|
|
|
bool POSIXSubsystemRunCommandAndCheckOutput(const char **executeEnvironment, const char **argv,
|
|
const char *executable, const char *expectedOutput) {
|
|
int stdoutPipe[2];
|
|
pipe(stdoutPipe);
|
|
|
|
long pid = vfork();
|
|
|
|
if (pid == 0) {
|
|
dup2(stdoutPipe[1], 1);
|
|
dup2(stdoutPipe[1], 2);
|
|
close(stdoutPipe[1]);
|
|
execve(executable, argv, executeEnvironment);
|
|
EsPrint("Could not execve.\n");
|
|
return false;
|
|
} else if (pid > 0) {
|
|
close(stdoutPipe[1]);
|
|
char readData[4096];
|
|
int readPosition = 0;
|
|
|
|
while (true) {
|
|
intptr_t bytesRead = read(stdoutPipe[0], readData + readPosition, sizeof(readData) - readPosition);
|
|
|
|
if (bytesRead <= 0) {
|
|
break;
|
|
} else {
|
|
readPosition += bytesRead;
|
|
}
|
|
}
|
|
|
|
if (readPosition != (int) EsCStringLength(expectedOutput)
|
|
|| EsMemoryCompare(readData, expectedOutput, readPosition)) {
|
|
EsPrint("Incorrect output: '%s'.\n", readPosition, readData);
|
|
return false;
|
|
}
|
|
|
|
int status;
|
|
wait4(-1, &status, 0, NULL);
|
|
|
|
if (status) {
|
|
EsPrint("status = %d.\n", status);
|
|
return false;
|
|
}
|
|
} else {
|
|
EsPrint("Could not vfork.\n");
|
|
return false;
|
|
}
|
|
|
|
close(stdoutPipe[0]);
|
|
return true;
|
|
}
|
|
|
|
bool POSIXSubsystemTest() {
|
|
int checkIndex = 0;
|
|
|
|
const char *executeEnvironment[] = {
|
|
"PATH=/Applications/POSIX/bin",
|
|
"TMPDIR=/Applications/POSIX/tmp",
|
|
NULL,
|
|
};
|
|
|
|
int _argc;
|
|
char **_argv;
|
|
EsPOSIXInitialise(&_argc, &_argv);
|
|
|
|
const char *executable = "/Applications/POSIX/bin/busybox";
|
|
const char *argv[] = { "busybox", "sh", "test.sh", NULL, };
|
|
|
|
EsFileWriteAll(EsLiteral("|POSIX:/test.sh"), EsLiteral("echo hello"));
|
|
CHECK(POSIXSubsystemRunCommandAndCheckOutput(executeEnvironment, argv, executable, "hello\n"));
|
|
EsFileWriteAll(EsLiteral("|POSIX:/test.sh"), EsLiteral("echo world"));
|
|
CHECK(POSIXSubsystemRunCommandAndCheckOutput(executeEnvironment, argv, executable, "world\n"));
|
|
EsFileWriteAll(EsLiteral("|POSIX:/test.sh"), EsLiteral("find . | grep Kernel.esx"));
|
|
CHECK(POSIXSubsystemRunCommandAndCheckOutput(executeEnvironment, argv, executable, "./Essence/Kernel.esx\n"));
|
|
|
|
EsFileWriteAll(EsLiteral("|POSIX:/test.sh"), EsLiteral("exit"));
|
|
|
|
for (uintptr_t i = 0; i < 1000; i++) {
|
|
CHECK(POSIXSubsystemRunCommandAndCheckOutput(executeEnvironment, argv, executable, ""));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
bool RestartTest() {
|
|
size_t fileSize;
|
|
uint32_t *fileData = (uint32_t *) EsFileReadAll(EsLiteral("|Settings:/restart_test.txt"), &fileSize);
|
|
uint32_t index = fileSize == sizeof(uint32_t) ? *fileData : 0;
|
|
#ifdef DEBUG_BUILD
|
|
if (index == 30) return true;
|
|
#else
|
|
if (index == 200) return true;
|
|
#endif
|
|
index++;
|
|
if (ES_SUCCESS != EsFileWriteAll(EsLiteral("|Settings:/restart_test.txt"), &index, sizeof(uint32_t))) return false;
|
|
EsPrint("Restart %d...\n", index);
|
|
EsSyscall(ES_SYSCALL_SHUTDOWN, ES_SHUTDOWN_ACTION_RESTART, 0, 0, 0);
|
|
while (EsMessageReceive());
|
|
return false;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
bool ResizeFileTest() {
|
|
int checkIndex = 0;
|
|
|
|
size_t dataBytes = 2 + 2 * 4096 + 2 * 262144;
|
|
uint8_t *data = (uint8_t *) EsHeapAllocate(dataBytes, false);
|
|
uint8_t *compare = (uint8_t *) EsHeapAllocate(dataBytes, false);
|
|
EsFileOffset currentSize = 0;
|
|
|
|
// Make a list of all the file sizes that should cover the interesting cases.
|
|
|
|
EsFileOffset sizes[125];
|
|
size_t sizeCount = 0;
|
|
|
|
for (int i = -2; i <= 2; i++) {
|
|
for (int j = -2; j <= 2; j++) {
|
|
for (int k = -2; k <= 2; k++) {
|
|
EsFileOffsetDifference size = k + j * 4096 /* page size */ + i * 262144 /* active section size */;
|
|
if (size < 0) continue;
|
|
EsAssert(sizeCount != sizeof(sizes) / sizeof(sizes[0]));
|
|
EsAssert((uintptr_t) size <= dataBytes);
|
|
sizes[sizeCount++] = size;
|
|
}
|
|
}
|
|
}
|
|
|
|
EsFileInformation file = EsFileOpen(EsLiteral("|Settings:/resize.txt"), ES_FILE_WRITE | ES_NODE_FAIL_IF_FOUND);
|
|
CHECK(file.error == ES_SUCCESS);
|
|
|
|
for (uintptr_t i = 0; i < sizeCount; i++) {
|
|
for (uintptr_t j = 0; j < sizeCount; j++) {
|
|
EsPrint("i %x, j %x\n", sizes[i], sizes[j]);
|
|
|
|
CHECK(ES_SUCCESS == EsFileResize(file.handle, sizes[i]));
|
|
if (sizes[i] > currentSize) EsMemoryZero(data + currentSize, sizes[i] - currentSize);
|
|
currentSize = sizes[i];
|
|
CHECK(currentSize == EsFileReadSync(file.handle, 0, dataBytes, compare));
|
|
CHECK(0 == EsMemoryCompare(data, compare, currentSize));
|
|
|
|
CHECK(ES_SUCCESS == EsFileResize(file.handle, sizes[j]));
|
|
if (sizes[j] > currentSize) EsMemoryZero(data + currentSize, sizes[j] - currentSize);
|
|
currentSize = sizes[j];
|
|
CHECK(currentSize == EsFileReadSync(file.handle, 0, dataBytes, compare));
|
|
CHECK(0 == EsMemoryCompare(data, compare, currentSize));
|
|
|
|
for (uintptr_t k = 0; k < currentSize; k++) {
|
|
data[k] = EsRandomU8();
|
|
}
|
|
|
|
EsFileWriteSync(file.handle, 0, currentSize, data);
|
|
}
|
|
}
|
|
|
|
EsHandleClose(file.handle);
|
|
EsHeapFree(data);
|
|
EsHeapFree(compare);
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
bool FileContentTypeTest() {
|
|
int checkIndex = 0;
|
|
|
|
EsUniqueIdentifier identifier1, identifier2;
|
|
for (uintptr_t i = 0; i < 16; i++) identifier1.d[i] = i;
|
|
for (uintptr_t i = 0; i < 16; i++) identifier2.d[i] = 0;
|
|
|
|
size_t fileSize;
|
|
uint32_t *fileData = (uint32_t *) EsFileReadAll(EsLiteral("|Settings:/continue.txt"), &fileSize);
|
|
|
|
if (fileData) {
|
|
size_t count;
|
|
EsError error;
|
|
EsDirectoryChild *array = EsDirectoryEnumerate(EsLiteral("|Settings:/test"), &count, &error);
|
|
CHECK(error == ES_SUCCESS);
|
|
CHECK(count == 1);
|
|
CHECK(0 == EsMemoryCompare(&identifier1, &array[0].contentType, sizeof(EsUniqueIdentifier)));
|
|
EsFileInformation information = EsFileOpen(EsLiteral("|Settings:/test/a"), ES_FILE_WRITE | ES_NODE_CREATE_DIRECTORIES);
|
|
CHECK(information.error == ES_SUCCESS);
|
|
CHECK(0 == EsMemoryCompare(&identifier1, &information.contentType, sizeof(EsUniqueIdentifier)));
|
|
} else {
|
|
uint32_t c = 1;
|
|
CHECK(ES_SUCCESS == EsFileWriteAll(EsLiteral("|Settings:/continue.txt"), &c, sizeof(uint32_t)));
|
|
|
|
EsFileInformation information = EsFileOpen(EsLiteral("|Settings:/test/a"), ES_FILE_WRITE | ES_NODE_CREATE_DIRECTORIES);
|
|
CHECK(information.error == ES_SUCCESS);
|
|
CHECK(0 == EsMemoryCompare(&identifier2, &information.contentType, sizeof(EsUniqueIdentifier)));
|
|
CHECK(ES_SUCCESS == EsFileControl(information.handle, ES_FILE_CONTROL_SET_CONTENT_TYPE, &identifier1, sizeof(identifier1)));
|
|
|
|
EsSyscall(ES_SYSCALL_SHUTDOWN, ES_SHUTDOWN_ACTION_RESTART, 0, 0, 0);
|
|
while (EsMessageReceive());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
#endif
|
|
|
|
const Test tests[] = {
|
|
TEST(BasicFileOperations, 60),
|
|
TEST(CRTMathFunctions, 60),
|
|
TEST(CRTStringFunctions, 60),
|
|
TEST(CRTOtherFunctions, 60),
|
|
TEST(PerformanceTimerDrift, 60),
|
|
TEST(TextboxEditOperations, 240),
|
|
TEST(OldTests2018, 60),
|
|
TEST(HeapReallocate, 60),
|
|
TEST(ArenaRandomAllocations, 60),
|
|
TEST(RangeSetTests, 60),
|
|
TEST(UTF8Tests, 60),
|
|
TEST(PipeTests, 60),
|
|
TEST(POSIXSubsystemTest, 120),
|
|
TEST(RestartTest, 1200),
|
|
TEST(ResizeFileTest, 600),
|
|
TEST(FileContentTypeTest, 60),
|
|
};
|
|
|
|
#ifndef API_TESTS_FOR_RUNNER
|
|
|
|
void RunTests() {
|
|
size_t fileSize;
|
|
EsError error;
|
|
void *fileData = EsFileReadAll(EsLiteral("|Settings:/test.dat"), &fileSize, &error);
|
|
|
|
if (error == ES_ERROR_FILE_DOES_NOT_EXIST) {
|
|
return; // Not in test mode.
|
|
} else if (error != ES_SUCCESS) {
|
|
EsPrint("Could not read test.dat (error %d).\n", error);
|
|
} else if (fileSize != sizeof(uint32_t) * 2) {
|
|
EsPrint("test.dat is the wrong size (got %d, expected %d).\n", fileSize, sizeof(uint32_t));
|
|
} else {
|
|
uint32_t index, mode;
|
|
EsMemoryCopy(&index, fileData, sizeof(uint32_t));
|
|
EsMemoryCopy(&mode, (uint8_t *) fileData + sizeof(uint32_t), sizeof(uint32_t));
|
|
EsHeapFree(fileData);
|
|
|
|
if (index >= sizeof(tests) / sizeof(tests[0])) {
|
|
EsPrint("Test index out of bounds.\n");
|
|
} else if (tests[index].callback()) {
|
|
EsPrint("[APITests-Success]\n");
|
|
} else {
|
|
EsPrint("[APITests-Failure]\n");
|
|
if (~mode & 1) EsSyscall(ES_SYSCALL_DEBUG_COMMAND, 2, 0, 0, 0);
|
|
}
|
|
}
|
|
|
|
EsSyscall(ES_SYSCALL_SHUTDOWN, ES_SHUTDOWN_ACTION_POWER_OFF, 0, 0, 0);
|
|
EsProcessTerminateCurrent();
|
|
}
|
|
|
|
void _start() {
|
|
_init();
|
|
|
|
while (true) {
|
|
EsMessage *message = EsMessageReceive();
|
|
|
|
if (message->type == ES_MSG_INSTANCE_CREATE) {
|
|
EsInstance *instance = EsInstanceCreate(message, EsLiteral("API Tests"));
|
|
EsApplicationStartupRequest request = EsInstanceGetStartupRequest(instance);
|
|
|
|
if (request.flags & ES_APPLICATION_STARTUP_BACKGROUND_SERVICE) {
|
|
RunTests();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|