/*
* Split testing for Tiny PNG Output (C)
*
* Copyright (c) 2018 Project Nayuki
* https://www.nayuki.io/page/tiny-png-output
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program (see COPYING.txt and COPYING.LESSER.txt).
* If not, see .
*/
#include
#include
#include
#include
#include
#include
#include "TinyPngOut.h"
static void test(uint32_t width, uint32_t height, long trials);
static void printErrorExit(enum TinyPngOut_Status status);
static uint32_t randUint32();
static FILE *randIn;
int main(void) {
randIn = fopen("/dev/urandom", "rb");
if (randIn == NULL) {
perror("Error: fopen /dev/urandom");
return EXIT_FAILURE;
}
for (long i = 0; ; ) {
uint32_t width = randUint32() % 100000;
uint32_t height = randUint32() % 100000;
if (width > 0 && height > 0 && (uint64_t)width * height * 3 < 1000000) {
fprintf(stderr, "Test %ld: width=%" PRIu32 " height=%" PRIu32 " pixels=%" PRIu32 " bytes=%" PRIu32 "\n",
i, width, height, width * height, width * height * 3);
test(width, height, 10);
if (i < LONG_MAX)
i++;
}
}
if (fclose(randIn) != 0) {
perror("Error: fclose");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
static void test(uint32_t width, uint32_t height, long trials) {
// Generate array of random pixel values
size_t numPixels = (size_t)width * height;
size_t numBytes = numPixels * 3;
uint8_t *pixelBytes = calloc(numBytes, sizeof(uint8_t));
if (pixelBytes == NULL) {
perror("Error: calloc");
exit(EXIT_FAILURE);
}
if (fread(pixelBytes, sizeof(pixelBytes[0]), numBytes, randIn) != numBytes) {
perror("Error: fread");
exit(EXIT_FAILURE);
}
// Write entire image in one shot
char *referenceData;
size_t referenceLen;
{
FILE *fout = open_memstream(&referenceData, &referenceLen); // POSIX API, not standard C library
if (fout == NULL) {
perror("Error: open_memstream");
exit(EXIT_FAILURE);
}
struct TinyPngOut pngout;
enum TinyPngOut_Status status = TinyPngOut_init(&pngout, width, height, fout);
if (status != TINYPNGOUT_OK)
printErrorExit(status);
status = TinyPngOut_write(&pngout, pixelBytes, numPixels);
if (status != TINYPNGOUT_OK)
printErrorExit(status);
if (fclose(fout) != 0) {
perror("Error: fclose");
exit(EXIT_FAILURE);
}
}
// Try writing in different splits
for (long i = 0; i < trials; i++) {
fprintf(stderr, " Trial %ld: ", i);
char *actualData;
size_t actualLen;
FILE *fout = open_memstream(&actualData, &actualLen);
if (fout == NULL) {
perror("Error: open_memstream");
exit(EXIT_FAILURE);
}
struct TinyPngOut pngout;
enum TinyPngOut_Status status = TinyPngOut_init(&pngout, width, height, fout);
if (status != TINYPNGOUT_OK)
printErrorExit(status);
size_t offset = 0;
while (offset < numPixels) {
size_t count = randUint32() % (numPixels - offset + 1);
fprintf(stderr, "%zu ", count);
fflush(stderr);
status = TinyPngOut_write(&pngout, pixelBytes + offset * 3, count);
if (status != TINYPNGOUT_OK)
printErrorExit(status);
offset += count;
}
if (fclose(fout) != 0) {
perror("Error: fclose");
exit(EXIT_FAILURE);
}
int cmp = actualLen == referenceLen ? 0 : 1;
if (cmp == 0)
cmp = memcmp(actualData, referenceData, actualLen);
free(actualData);
fprintf(stderr, "%s\n", (cmp == 0 ? "Same" : "Different"));
if (cmp != 0)
exit(EXIT_FAILURE);
}
free(pixelBytes);
free(referenceData);
}
static void printErrorExit(enum TinyPngOut_Status status) {
const char *msg;
switch (status) {
case TINYPNGOUT_OK : msg = "OK"; break;
case TINYPNGOUT_INVALID_ARGUMENT: msg = "Invalid argument"; break;
case TINYPNGOUT_IMAGE_TOO_LARGE : msg = "Image too large"; break;
case TINYPNGOUT_IO_ERROR : msg = "I/O error"; break;
default : msg = "Unknown error"; break;
}
fprintf(stderr, "Error: %s\n", msg);
exit(EXIT_FAILURE);
}
static uint32_t randUint32() {
uint8_t b[4];
if (fread(b, sizeof(uint8_t), 4, randIn) != 4) {
perror("Error: fread");
exit(EXIT_FAILURE);
}
return (uint32_t)b[0] << 0
| (uint32_t)b[1] << 8
| (uint32_t)b[2] << 16
| (uint32_t)b[3] << 24;
}