Test: Initial CInema testing

This commit is contained in:
Vicki Pfau 2020-06-24 04:37:58 -07:00
parent d6f041d5e7
commit 5ceec84560
2 changed files with 172 additions and 18 deletions

View File

@ -42,4 +42,5 @@ if(BUILD_CINEMA)
enable_testing()
add_executable(${BINARY_NAME}-cinema ${CMAKE_CURRENT_SOURCE_DIR}/cinema-main.c)
target_link_libraries(${BINARY_NAME}-cinema ${BINARY_NAME} ${PLATFORM_LIBRARY})
set_target_properties(${BINARY_NAME}-cinema PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FEATURE_DEFINES};${FUNCTION_DEFINES}")
endif()

View File

@ -10,6 +10,7 @@
#include <mgba/feature/commandline.h>
#include <mgba/feature/video-logger.h>
#include <mgba-util/png-io.h>
#include <mgba-util/vector.h>
#include <mgba-util/vfs.h>
@ -40,6 +41,7 @@ enum CInemaStatus {
CI_FAIL,
CI_XPASS,
CI_XFAIL,
CI_ERROR,
CI_SKIP
};
@ -47,7 +49,6 @@ struct CInemaTest {
char directory[MAX_TEST];
char filename[MAX_TEST];
char name[MAX_TEST];
struct mCoreConfig config;
enum CInemaStatus status;
int failedFrames;
};
@ -55,12 +56,20 @@ struct CInemaTest {
DECLARE_VECTOR(CInemaTestList, struct CInemaTest)
DEFINE_VECTOR(CInemaTestList, struct CInemaTest)
DECLARE_VECTOR(ImageList, void*)
DEFINE_VECTOR(ImageList, void*)
static bool showVersion = false;
static bool showUsage = false;
static char base[PATH_MAX] = {0};
static bool dryRun = false;
static int verbosity = 0;
bool CInemaTestInit(struct CInemaTest*, const char* directory, const char* filename);
void CInemaTestRun(struct CInemaTest*);
static void _log(struct mLogger* log, int category, enum mLogLevel level, const char* format, va_list args);
ATTRIBUTE_FORMAT(printf, 2, 3) void CIlog(int minlevel, const char* format, ...) {
if (verbosity < minlevel) {
return;
@ -81,9 +90,6 @@ ATTRIBUTE_FORMAT(printf, 2, 3) void CIerr(int minlevel, const char* format, ...)
va_end(args);
}
bool CInemaTestInit(struct CInemaTest*, const char* directory, const char* filename);
void CInemaTestDeinit(struct CInemaTest*);
static bool parseCInemaArgs(int argc, char* const* argv) {
int ch;
int index = 0;
@ -139,7 +145,7 @@ static bool determineBase(int argc, char* const* argv) {
}
static bool collectTests(struct CInemaTestList* tests, const char* path) {
CIerr(1, "Considering path %s\n", path);
CIerr(2, "Considering path %s\n", path);
struct VDir* dir = VDirOpen(path);
if (!dir) {
return false;
@ -197,7 +203,6 @@ static void reduceTestList(struct CInemaTestList* tests) {
++i;
continue;
}
CInemaTestDeinit(cur);
CInemaTestListShift(tests, i, 1);
}
}
@ -218,7 +223,134 @@ bool CInemaTestInit(struct CInemaTest* test, const char* directory, const char*
return true;
}
void CInemaTestDeinit(struct CInemaTest* test) {
void CInemaTestRun(struct CInemaTest* test) {
struct VDir* dir = VDirOpen(test->directory);
if (!dir) {
CIerr(0, "Failed to open test directory\n");
test->status = CI_ERROR;
return;
}
struct VFile* rom = dir->openFile(dir, test->filename, O_RDONLY);
if (!rom) {
CIerr(0, "Failed to open test\n");
test->status = CI_ERROR;
return;
}
struct mCore* core = mCoreFindVF(rom);
if (!core) {
CIerr(0, "Failed to load test\n");
test->status = CI_ERROR;
rom->close(rom);
return;
}
if (!core->init(core)) {
CIerr(0, "Failed to init test\n");
test->status = CI_ERROR;
core->deinit(core);
return;
}
unsigned width, height;
core->desiredVideoDimensions(core, &width, &height);
ssize_t bufferSize = width * height * BYTES_PER_PIXEL;
void* buffer = malloc(bufferSize);
if (!buffer) {
CIerr(0, "Failed to allocate video buffer\n");
test->status = CI_ERROR;
core->deinit(core);
}
core->setVideoBuffer(core, buffer, width);
mCoreConfigInit(&core->config, "cinema");
core->loadROM(core, rom);
core->reset(core);
test->status = CI_PASS;
unsigned minFrame = core->frameCounter(core);
unsigned limit = 9999;
size_t frame;
for (frame = 0; limit; ++frame, --limit) {
char baselineName[32];
snprintf(baselineName, sizeof(baselineName), "baseline_%04" PRIz "u.png", frame);
core->runFrame(core);
unsigned frameCounter = core->frameCounter(core);
if (frameCounter <= minFrame) {
break;
}
CIerr(2, "Test frame: %u\n", frameCounter);
struct VFile* baselineVF = dir->openFile(dir, baselineName, O_RDONLY);
if (!baselineVF) {
test->status = CI_FAIL;
} else {
png_structp png = PNGReadOpen(baselineVF, 0);
png_infop info = png_create_info_struct(png);
png_infop end = png_create_info_struct(png);
if (!png || !info || !end || !PNGReadHeader(png, info)) {
PNGReadClose(png, info, end);
CIerr(1, "Failed to load %s\n", baselineName);
test->status = CI_ERROR;
} else {
unsigned pwidth = png_get_image_width(png, info);
unsigned pheight = png_get_image_height(png, info);
unsigned twidth, theight;
core->desiredVideoDimensions(core, &twidth, &theight);
if (pheight != theight || pwidth != twidth) {
PNGReadClose(png, info, end);
CIerr(1, "Size mismatch for %s, expected %ux%u, got %ux%u\n", baselineName, pwidth, pheight, twidth, theight);
test->status = CI_FAIL;
} else {
uint8_t* pixels = malloc(pwidth * pheight * BYTES_PER_PIXEL);
if (!pixels) {
CIerr(1, "Failed to allocate baseline buffer\n");
test->status = CI_ERROR;
} else {
if (!PNGReadPixels(png, info, pixels, pwidth, pheight, pwidth) || !PNGReadFooter(png, end)) {
CIerr(1, "Failed to read %s\n", baselineName);
test->status = CI_ERROR;
} else {
uint8_t* testPixels = buffer;
size_t x;
size_t y;
for (y = 0; y < theight; ++y) {
for (x = 0; x < twidth; ++x) {
size_t pix = pwidth * y + x;
size_t tpix = width * y + x;
int testR = testPixels[tpix * 4 + 0];
int testG = testPixels[tpix * 4 + 1];
int testB = testPixels[tpix * 4 + 2];
int expectR = pixels[pix * 4 + 0];
int expectG = pixels[pix * 4 + 1];
int expectB = pixels[pix * 4 + 2];
int r = expectR - testR;
int g = expectG - testG;
int b = expectB - testB;
if (r | g | b) {
CIerr(2, "Frame %u failed at pixel %" PRIz "ux%" PRIz "u with diff %i,%i,%i (expected %02x%02x%02x, got %02x%02x%02x)\n",
frameCounter, x, y, r, g, b,
expectR, expectG, expectB,
testR, testG, testB);
test->status = CI_FAIL;
++test->failedFrames;
}
}
}
}
}
PNGReadClose(png, info, end);
free(pixels);
}
}
baselineVF->close(baselineVF);
}
}
free(buffer);
core->deinit(core);
dir->close(dir);
}
void _log(struct mLogger* log, int category, enum mLogLevel level, const char* format, va_list args) {
// TODO: Write
}
@ -256,6 +388,9 @@ int main(int argc, char** argv) {
struct CInemaTestList tests;
CInemaTestListInit(&tests, 0);
struct mLogger logger = { .log = _log };
mLogSetDefaultLogger(&logger);
if (argc > 0) {
size_t i;
for (i = 0; i < (size_t) argc; ++i) {
@ -298,20 +433,38 @@ int main(int argc, char** argv) {
reduceTestList(&tests);
}
if (dryRun) {
size_t i;
for (i = 0; i < CInemaTestListSize(&tests); ++i) {
struct CInemaTest* test = CInemaTestListGetPointer(&tests, i);
CIlog(-1, "%s\n", test->name);
}
} else {
// TODO: Write
}
size_t i;
for (i = 0; i < CInemaTestListSize(&tests); ++i) {
struct CInemaTest* test = CInemaTestListGetPointer(&tests, i);
CInemaTestDeinit(test);
if (dryRun) {
CIlog(-1, "%s\n", test->name);
} else {
CIerr(1, "%s: ", test->name);
CInemaTestRun(test);
switch (test->status) {
case CI_PASS:
CIerr(1, "pass");
break;
case CI_FAIL:
status = 1;
CIerr(1, "fail");
break;
case CI_XPASS:
CIerr(1, "xpass");
break;
case CI_XFAIL:
CIerr(1, "xfail");
break;
case CI_SKIP:
CIerr(1, "skip");
break;
case CI_ERROR:
status = 1;
CIerr(1, "error");
break;
}
CIerr(1, "\n");
}
}
CInemaTestListDeinit(&tests);