#define _POSIX_C_SOURCE 200809L #include "xtc/xtc_project.h" #include #include #include #include #include #include #include #include /* Linker interposition injects errors only into this test executable. */ static int fault; int __real_fflush(FILE *file); int __real_fclose(FILE *file); int __real_fsync(int descriptor); int __real_rename(const char *old, const char *next); int __wrap_fflush(FILE *file) { if (fault == 1) { errno = EIO; return EOF; } return __real_fflush(file); } int __wrap_fclose(FILE *file) { int result = __real_fclose(file); if (fault == 4) { errno = EIO; return EOF; } return result; } int __wrap_fsync(int descriptor) { struct stat info; assert(fstat(descriptor,&info) == 0); if ((fault == 2 && S_ISREG(info.st_mode)) || (fault == 5 && S_ISDIR(info.st_mode))) { errno = EIO; return -1; } return __real_fsync(descriptor); } int __wrap_rename(const char *old, const char *next) { if (fault == 3) { errno = EIO; return -1; } return __real_rename(old,next); } static void write_text(const char *path, const char *text) { FILE *file = fopen(path,"wb"); assert(file); assert(fwrite(text,1,strlen(text),file) == strlen(text)); assert(fclose(file) == 0); } static size_t bytes(const char *path, char out[4096]) { FILE *file = fopen(path,"rb"); size_t n; assert(file); n = fread(out,1,4096,file); assert(!ferror(file) && feof(file)); assert(fclose(file) == 0); return n; } static void no_temporaries(const char *path) { DIR *directory = opendir(path); struct dirent *entry; assert(directory); while ((entry = readdir(directory))) assert(!strstr(entry->d_name,".tmp.")); assert(closedir(directory) == 0); } int main(void) { char root[] = "/tmp/xtc-project-report-XXXXXX", song[256], bad[256]; char before[4096], after[4096]; size_t size, n; xtc_project *old = NULL, *next = NULL, *loaded = NULL; xtc_project_diagnostic diagnostic; xtc_track_info track; uint64_t id; int mode; assert(mkdtemp(root)); snprintf(song,sizeof(song),"%s/song.xtc",root); snprintf(bad,sizeof(bad),"%s/bad.xtc",root); assert(xtc_project_create(&old) == XTC_OK && xtc_project_create(&next) == XTC_OK && xtc_project_create(&loaded) == XTC_OK); assert(xtc_project_add_track(old,"Old",&id) == XTC_OK && xtc_project_add_track(next,"New",&id) == XTC_OK); assert(xtc_project_save(old,song) == XTC_OK); size = bytes(song,before); assert(xtc_project_load_diagnostic(loaded,song,&diagnostic) == XTC_OK && diagnostic.result == XTC_OK && diagnostic.format_version == XTC_PROJECT_FORMAT_VERSION && !diagnostic.line); for (mode = 1; mode <= 5; ++mode) { fault = mode; assert(xtc_project_save(next,song) == XTC_ERR_IO); fault = 0; no_temporaries(root); n = bytes(song,after); if (mode < 5) assert(n == size && !memcmp(before,after,size)); assert(xtc_project_load(loaded,song) == XTC_OK); assert(xtc_project_get_track(loaded,0,&track) == XTC_OK && !strcmp(track.name,mode < 5 ? "Old" : "New")); } write_text(bad,"XTC_PROJECT 999\n"); assert(xtc_project_load_diagnostic(loaded,bad,&diagnostic) == XTC_ERR_UNSUPPORTED && diagnostic.line == 1 && diagnostic.format_version == 999 && !strcmp(diagnostic.record,"XTC_PROJECT") && strstr(diagnostic.message,"1..17")); write_text(bad,"XTC_PROJECT 10\nAPP 0 1 0\nTEMPO nope\n"); assert(xtc_project_load_diagnostic(loaded,bad,&diagnostic) == XTC_ERR_FORMAT && diagnostic.line == 3 && !strcmp(diagnostic.record,"TEMPO")); write_text(bad,"XTC_PROJECT 10\n"); assert(xtc_project_load_diagnostic(loaded,bad,&diagnostic) == XTC_ERR_FORMAT && diagnostic.line == 2 && !strcmp(diagnostic.record,"APP")); write_text(bad,"XTC_PROJECT 10\nAPP 0 1 0\nTEMPO 120000\nIDS 0 0\nGRAPH 1 1 1 0\nNODE 0 7 00000000\n"); assert(xtc_project_load_diagnostic(loaded,bad,&diagnostic) == XTC_ERR_UNSUPPORTED && diagnostic.line == 6 && !strcmp(diagnostic.record,"NODE") && strstr(diagnostic.message,"node type")); { FILE *file = fopen(bad,"wb"); assert(file); assert(ftruncate(fileno(file),4194305) == 0 && fclose(file) == 0); assert(xtc_project_load_diagnostic(loaded,bad,&diagnostic) == XTC_ERR_FORMAT && !diagnostic.line && !strcmp(diagnostic.record,"SIZE") && strstr(diagnostic.message,"4 MiB")); } { FILE *file = fopen(bad,"wb"); size_t i; assert(file); for (i = 0; i < 1536; ++i) assert(fputc('A',file) != EOF); assert(fputc('\n',file) != EOF && fclose(file) == 0); assert(xtc_project_load_diagnostic(loaded,bad,&diagnostic) == XTC_ERR_FORMAT && diagnostic.line == 1); } assert(xtc_project_get_track(loaded,0,&track) == XTC_OK && !strcmp(track.name,"New")); assert(xtc_project_load_diagnostic(loaded,"/absent/xtc-report.xtc",&diagnostic) == XTC_ERR_IO && !diagnostic.line); xtc_project_destroy(old); xtc_project_destroy(next); xtc_project_destroy(loaded); unlink(song); unlink(bad); assert(rmdir(root) == 0); puts("Project load diagnostics, retained state, bounded input and pre/post-rename save faults passed."); return 0; }