diff --git a/include/ft/all-tests.h b/include/ft/all-tests.h index 1fd8ebd..2effa24 100644 --- a/include/ft/all-tests.h +++ b/include/ft/all-tests.h @@ -33,6 +33,9 @@ extern ft_test ft_stdio_simple_write; extern ft_test ft_stdio_line_buffering; extern ft_test ft_stdio_update_ungetc; extern ft_test ft_stdio_append; +extern ft_test ft_stdio_fdopen; +extern ft_test ft_stdio_write_chars; +extern ft_test ft_stdio_read_chars; /* stdlib */ extern ft_test ft_stdlib_arith; diff --git a/src/main.c b/src/main.c index 13eba97..d4083c0 100644 --- a/src/main.c +++ b/src/main.c @@ -49,6 +49,9 @@ ft_list headers_libc[] = { &ft_stdio_line_buffering, &ft_stdio_update_ungetc, &ft_stdio_append, + &ft_stdio_fdopen, + &ft_stdio_write_chars, + &ft_stdio_read_chars, NULL, }}, { _("stdlib.h", ""), (ft_test*[]){ diff --git a/src/stdio/files.c b/src/stdio/files.c index a72be32..034397b 100644 --- a/src/stdio/files.c +++ b/src/stdio/files.c @@ -670,3 +670,287 @@ ft_test ft_stdio_append = { .name = "Append modes", .function = _ft_stdio_append, }; + +static void _ft_stdio_fdopen_switch(ft_test *t) +{ + int fd, rc; + FILE *fp; + char str[128]; + + /* Write some stuff through fdopen() */ + DO_E(fd, creat("ft_fdopen.txt", 0755), t, "%d"); + ft_assert(t, fd >= 0); + if(fd < 0) return; + DO_E(fp, fdopen(fd, "w"), t, "%p"); + ft_assert(t, fp != NULL); + if(fp == NULL) { + close(fd); + return; + } + DO_E(rc, fwrite(filler, 1, 128, fp), t, "%d"); + ft_assert(t, rc == 128); + DO_E(rc, fclose(fp), t, "%d"); + ft_assert(t, rc == 0); + + /* Check that the contents are correct */ + DO_E(fp, fopen("ft_fdopen.txt", "r"), t, "%p"); + ft_assert(t, fp != NULL); + if(fp == NULL) return; + DO_E(rc, fread(str, 1, 128, fp), t, "%d"); + ft_assert(t, rc == 128); + ft_assert(t, !memcmp(str, filler, 128)); + DO_E(rc, fclose(fp), t, "%d"); + ft_assert(t, rc == 0); +} + +static void _ft_stdio_fdopen(ft_test *t) +{ + gint_world_switch(GINT_CALL(_ft_stdio_fdopen_switch, (void *)t)); +} + +ft_test ft_stdio_fdopen = { + .name = "FILE wrapper with fdopen()", + .function = _ft_stdio_fdopen, +}; + +static void _ft_stdio_write_chars_switch(ft_test *t) +{ + FILE *fp; + int rc; + + /* Create a buffered file with basic data */ + DO_E(fp, fopen("ft_fput.txt", "w"), t, "%p"); + ft_assert(t, fp != NULL); + if(fp == NULL) return; + ft_log_FILE(t, "", fp); + DO_E(rc, setvbuf(fp, NULL, _IOFBF, 32), t, "%d"); + ft_assert(t, rc == 0); + DO_E(rc, fputs("Test!", fp), t, "%d"); + ft_assert(t, rc >= 0); + ft_log_FILE(t, "",fp); + ft_assert(t, fp->fdpos == 0); + ft_assert(t, fp->bufpos == 5); + DO_E(rc, fputs("Testing fputs() on _IOFBF streams\n", fp), t, "%d"); + ft_assert(t, rc >= 0); + ft_log_FILE(t, "",fp); + ft_assert(t, fp->fdpos == 32); + ft_assert(t, fp->bufpos == 7); + for(int i = 0; i < 26; i++) { + DO_E(rc, fputc('A'+i, fp), t, "'%c'"); + ft_assert(t, rc == 'A'+i); + DO_E(rc, fputc('a'+i, fp), t, "'%c'"); + ft_assert(t, rc == 'a'+i); + } + ft_log_FILE(t, "", fp); + ft_assert(t, fp->fdpos == 64); + ft_assert(t, fp->bufpos == 27); + DO_E(rc, fclose(fp), t, "%d"); + ft_assert(t, rc == 0); + + /* Same, but make it line-buffered */ + DO_E(fp, fopen("ft_fput.txt", "w"), t, "%p"); + ft_assert(t, fp != NULL); + if(fp == NULL) return; + ft_log_FILE(t, "", fp); + DO_E(rc, setvbuf(fp, NULL, _IOLBF, 32), t, "%d"); + ft_assert(t, rc == 0); + DO_E(rc, fputs("Testing fputs() on _IOLBF streams\n", fp), t, "%d"); + ft_assert(t, rc >= 0); + ft_log_FILE(t, "",fp); + ft_assert(t, fp->fdpos == 34); + ft_assert(t, fp->bufpos == 0); + for(int i = 0; i < 26; i++) { + DO_E(rc, fputc('a'+i, fp), t, "'%c'"); + ft_assert(t, rc == 'a'+i); + } + ft_log_FILE(t, "", fp); + ft_assert(t, fp->fdpos == 34); + ft_assert(t, fp->bufpos == 26); + DO_E(rc, putc('\n', fp), t, "%d"); + ft_assert(t, rc == '\n'); + ft_log_FILE(t, "", fp); + ft_assert(t, fp->fdpos == 61); + ft_assert(t, fp->bufpos == 0); + DO_E(rc, fclose(fp), t, "%d"); + ft_assert(t, rc == 0); + + /* Same with unbuffered */ + DO_E(fp, fopen("ft_fput.txt", "w"), t, "%p"); + ft_assert(t, fp != NULL); + if(fp == NULL) return; + ft_log_FILE(t, "", fp); + DO_E(rc, setvbuf(fp, NULL, _IONBF, 0), t, "%d"); + ft_assert(t, rc == 0); + DO_E(rc, fputs("Testing fputs() on _IONBF streams\n", fp), t, "%d"); + ft_assert(t, rc >= 0); + ft_log_FILE(t, "",fp); + /* Urgh this is going to be so slow. Don't do that kids */ + for(int i = 0; i < 26; i++) { + DO_E(rc, putc('a'+i, fp), t, "'%c'"); + ft_assert(t, rc == 'a'+i); + } + ft_log_FILE(t, "", fp); + DO_E(rc, fclose(fp), t, "%d"); + ft_assert(t, rc == 0); +} + +static void _ft_stdio_write_chars(ft_test *t) +{ + gint_world_switch(GINT_CALL(_ft_stdio_write_chars_switch, (void *)t)); +} + +ft_test ft_stdio_write_chars = { + .name = "Character output functions", + .function = _ft_stdio_write_chars, +}; + +static void _ft_stdio_read_chars_switch(ft_test *t) +{ + FILE *fp; + int rc; + char str[20], *retstr; + + /* Create an initial file with some filler */ + DO_E(fp, fopen("ft_fget.txt", "w"), t, "%p"); + ft_assert(t, fp != NULL); + if(fp == NULL) return; + DO_E(rc, fputs("Line #1\nLine #2\nLine #3", fp), t, "%d"); + ft_assert(t, rc >= 0); + DO_E(rc, fclose(fp), t, "%d"); + ft_assert(t, rc == 0); + + /* Read it with fgets() and fgetc() */ + DO_E(fp, fopen("ft_fget.txt", "r"), t, "%p"); + ft_assert(t, fp != NULL); + if(fp == NULL) return; + DO_E(rc, setvbuf(fp, NULL, _IOFBF, 14), t, "%d"); + ft_assert(t, rc == 0); + ft_log_FILE(t, "", fp); + memset(str, 0xff, sizeof str); + DO_E(retstr, fgets(str, 20, fp), t, "%p"); + ft_assert(t, retstr != NULL); + ft_log_FILE(t, "", fp); + ft_log(t, "str = '%s'\n", str); + ft_assert(t, !strcmp(str, "Line #1\n")); + memset(str, 0xff, sizeof str); + DO_E(retstr, fgets(str, 6, fp), t, "%p"); + ft_assert(t, retstr != NULL); + ft_log_FILE(t, "", fp); + ft_log(t, "str = '%s'\n", str); + ft_assert(t, !strcmp(str, "Line ")); + memset(str, 0xff, sizeof str); + DO_E(retstr, fgets(str, 20, fp), t, "%p"); + ft_assert(t, retstr != NULL); + ft_log_FILE(t, "", fp); + ft_assert(t, !strcmp(str, "#2\n")); + ft_log(t, "Reading to EOF with fgetc()...\n"); + for(int i = 0; !feof(fp); i++) { + int c = fgetc(fp); + if(c == EOF) break; + str[i] = c; + str[i+1] = 0; + } + ft_log(t, "str = '%s'\n", str); + ft_assert(t, !strcmp(str, "Line #3")); + + /* Test fgets() at end of file */ + DO_E(rc, fseek(fp, 16, SEEK_SET), t, "%d"); + ft_assert(t, rc == 0); + DO_E(retstr, fgets(str, 20, fp), t, "%p"); + ft_assert(t, retstr != NULL); + ft_log(t, "str = '%s'\n", str); + ft_assert(t, !strcmp(str, "Line #3")); + + /* Test interactions of fgets() and fgetc() with ungetc() */ + DO_E(rc, fseek(fp, 8, SEEK_SET), t, "%d"); + ft_assert(t, rc == 0); + DO_E(rc, ungetc('#', fp), t, "'%c'"); + ft_assert(t, rc == '#'); + DO_E(rc, ungetc('!', fp), t, "'%c'"); + ft_assert(t, rc == '!'); + DO_E(rc, ungetc('$', fp), t, "'%c'"); + ft_assert(t, rc == '$'); + ft_log_FILE(t, "", fp); + DO_E(retstr, fgets(str, 20, fp), t, "%p"); + ft_assert(t, retstr != NULL); + ft_log_FILE(t, "", fp); + ft_log(t, "str = '%s'\n", str); + ft_assert(t, !strcmp(str, "$!#Line #2\n")); + DO_E(rc, ungetc('#', fp), t, "'%c'"); + ft_assert(t, rc == '#'); + DO_E(rc, ungetc('!', fp), t, "'%c'"); + ft_assert(t, rc == '!'); + DO_E(rc, ungetc('$', fp), t, "'%c'"); + ft_assert(t, rc == '$'); + ft_log_FILE(t, "", fp); + ft_log(t, "Reading to EOF with fgetc()...\n"); + for(int i = 0; !feof(fp); i++) { + int c = getc(fp); + if(c == EOF) break; + str[i] = c; + str[i+1] = 0; + } + ft_log_FILE(t, "", fp); + ft_log(t, "str = '%s'\n", str); + ft_assert(t, !strcmp(str, "$!#Line #3")); + + /* Try again without buffering */ + rewind(fp); + ft_log(t, "rewind(fp)\n"); + DO_E(rc, setvbuf(fp, NULL, _IONBF, 0), t, "%d"); + ft_assert(t, rc == 0); + memset(str, 0xff, sizeof str); + DO_E(retstr, fgets(str, 20, fp), t, "%p"); + ft_assert(t, retstr != NULL); + ft_log_FILE(t, "", fp); + ft_log(t, "str = '%s'\n", str); + ft_assert(t, !strcmp(str, "Line #1\n")); + memset(str, 0xff, sizeof str); + DO_E(retstr, fgets(str, 6, fp), t, "%p"); + ft_assert(t, retstr != NULL); + ft_log_FILE(t, "", fp); + ft_log(t, "str = '%s'\n", str); + ft_assert(t, !strcmp(str, "Line ")); + memset(str, 0xff, sizeof str); + DO_E(retstr, fgets(str, 20, fp), t, "%p"); + ft_assert(t, retstr != NULL); + ft_log_FILE(t, "", fp); + ft_assert(t, !strcmp(str, "#2\n")); + ft_log(t, "Reading to EOF with fgetc()...\n"); + for(int i = 0; !feof(fp); i++) { + int c = fgetc(fp); + if(c == EOF) break; + str[i] = c; + str[i+1] = 0; + } + ft_log(t, "str = '%s'\n", str); + ft_assert(t, !strcmp(str, "Line #3")); + + /* Re-test interactions with ungetc() */ + DO_E(rc, fseek(fp, 8, SEEK_SET), t, "%d"); + ft_assert(t, rc == 0); + DO_E(rc, ungetc('#', fp), t, "'%c'"); + ft_assert(t, rc == '#'); + DO_E(rc, ungetc('!', fp), t, "'%c'"); + ft_assert(t, rc == '!'); + DO_E(rc, ungetc('$', fp), t, "'%c'"); + ft_assert(t, rc == '$'); + ft_log_FILE(t, "", fp); + DO_E(retstr, fgets(str, 20, fp), t, "%p"); + ft_assert(t, retstr != NULL); + ft_log_FILE(t, "", fp); + ft_log(t, "str = '%s'\n", str); + ft_assert(t, !strcmp(str, "$!#Line #2\n")); + DO_E(rc, fclose(fp), t, "%d"); + ft_assert(t, rc == 0); +} + +static void _ft_stdio_read_chars(ft_test *t) +{ + gint_world_switch(GINT_CALL(_ft_stdio_read_chars_switch, (void *)t)); +} + +ft_test ft_stdio_read_chars = { + .name = "Character input functions", + .function = _ft_stdio_read_chars, +};