├── assets ├── output.png ├── output2.png └── output3.png ├── inc ├── main.h ├── color.h └── test.h ├── src ├── utils.c ├── runTest.c ├── main.c └── test.c ├── README.md └── test.sh /assets/output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmarcha/pipexMedic/HEAD/assets/output.png -------------------------------------------------------------------------------- /assets/output2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmarcha/pipexMedic/HEAD/assets/output2.png -------------------------------------------------------------------------------- /assets/output3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmarcha/pipexMedic/HEAD/assets/output3.png -------------------------------------------------------------------------------- /inc/main.h: -------------------------------------------------------------------------------- 1 | #ifndef MAIN_H 2 | # define MAIN_H 3 | 4 | # ifdef __APPLE__ 5 | # define _GNU_SOURCE 6 | # endif 7 | 8 | # include "test.h" 9 | # include "color.h" 10 | 11 | extern volatile sig_atomic_t testDone; 12 | 13 | /// /////////////////////////////// /// 14 | /// /// UTILS /// /// 15 | /// /////////////////////////////// /// 16 | 17 | size_t strslen(char *const *strs); 18 | void setSignal(void); 19 | 20 | #endif // MAIN_H 21 | -------------------------------------------------------------------------------- /src/utils.c: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | size_t strslen(char *const *strs) { 4 | 5 | size_t len; 6 | for (len = 0; strs[len] != NULL; len++); 7 | return (len); 8 | } 9 | 10 | void syncSignal(int sig) { 11 | 12 | (void)sig; 13 | testDone = 1; 14 | } 15 | 16 | void setSignal(void) { 17 | 18 | struct sigaction act; 19 | sigset_t blockmask; 20 | 21 | sigfillset(&blockmask); 22 | act.sa_handler = syncSignal; 23 | act.sa_mask = blockmask; 24 | act.sa_flags = 0; 25 | sigaction(SIGUSR1, &act, NULL); 26 | } 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pipex Medic 2 | 3 | Warning: Tester is designed to run on Linux environment only! 4 | 5 | A complete C program to test Pipex, a 42 project to learn fork, redirection, and pipe in C programming. \ 6 | Features: output files, standard streams, exit codes, parallelism, and file permissions comparison. 7 | 8 | # How to use it ? 9 | - Clone the repository in your Pipex directory, go in the tester directory, and run `bash test.sh` with a test subset. \ 10 | For example, `bash test.sh basic`. You can have a subset list if you launch the script without argument. 11 | - You can check the complete error listing in the log file: `cat tester.log | less`, or browse it in your favorite IDE. 12 | - Error tests are very punitive. There is no need to pass them to validate the project. 13 | 14 | ![output](/assets/output3.png) 15 | -------------------------------------------------------------------------------- /src/runTest.c: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | void runTest(const char *testSubset, const size_t testIndex, char *const commandList[], char *const envp[], const char* inputFileContent) { 4 | 5 | pid_t pid = fork(); 6 | 7 | if (pid == (pid_t)-1) { 8 | ferror("fork", errno); 9 | } 10 | 11 | else if (pid == (pid_t)0) { 12 | 13 | test(testSubset, testIndex, commandList, envp, inputFileContent); 14 | kill(getppid(), SIGUSR1); 15 | exit(0); 16 | } 17 | 18 | const uint64_t startTime = time(NULL); 19 | while (testDone == 0) { 20 | const uint64_t currentTime = time(NULL); 21 | if (currentTime - startTime > TIMEOUT) { 22 | printf(FG_RED"%lu. TIMEOUT (NO TEST PERFORMED)"RESET, testIndex); 23 | kill(pid, SIGKILL); 24 | break; 25 | } 26 | } 27 | 28 | testDone = 0; 29 | unlink("./input"); 30 | unlink("./outputPipex"); 31 | unlink("./outputBash"); 32 | } 33 | -------------------------------------------------------------------------------- /inc/color.h: -------------------------------------------------------------------------------- 1 | #ifndef COLOR_H 2 | # define COLOR_H 3 | 4 | # define RESET "\033[0m" 5 | 6 | # define FG_BLACK "\033[30m" 7 | # define FG_RED "\033[31m" 8 | # define FG_GREEN "\033[32m" 9 | # define FG_YELLOW "\033[33m" 10 | # define FG_BLUE "\033[34m" 11 | # define FG_MAGENTA "\033[35m" 12 | # define FG_CYAN "\033[36m" 13 | # define FG_LGRAY "\033[37m" 14 | 15 | # define FG_DGRAY "\033[90m" 16 | # define FG_LRED "\033[91m" 17 | # define FG_LGREEN "\033[92m" 18 | # define FG_LYELLOW "\033[93m" 19 | # define FG_LBLUE "\033[94m" 20 | # define FG_LMAGENTA "\033[95m" 21 | # define FG_LCYAN "\033[96m" 22 | # define FG_WHITE "\033[97m" 23 | 24 | # define BG_BLACK "\033[40m" 25 | # define BG_RED "\033[41m" 26 | # define BG_GREEN "\033[42m" 27 | # define BG_YELLOW "\033[43m" 28 | # define BG_BLUE "\033[44m" 29 | # define BG_MAGENTA "\033[45m" 30 | # define BG_CYAN "\033[46m" 31 | # define BG_LGRAY "\033[47m" 32 | 33 | # define BG_DGRAY "\033[100m" 34 | # define BG_LRED "\033[101m" 35 | # define BG_LGREEN "\033[102m" 36 | # define BG_LYELLOW "\033[103m" 37 | # define BG_LBLUE "\033[104m" 38 | # define BG_LMAGENTA "\033[105m" 39 | # define BG_LCYAN "\033[106m" 40 | # define BG_WHITE "\033[107m" 41 | 42 | #endif // COLOR_H 43 | -------------------------------------------------------------------------------- /inc/test.h: -------------------------------------------------------------------------------- 1 | #ifndef TEST_H 2 | # define TEST_H 3 | 4 | # include 5 | # include 6 | # include 7 | # include 8 | # include 9 | # include 10 | # include 11 | # include 12 | # include 13 | # include 14 | # include 15 | # include 16 | # include 17 | # include 18 | 19 | # define PROGRAM_NAME "pipexMedic" 20 | # define TIMEOUT 10 21 | # define DEFAULT_PATH "PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin" 22 | # define DEFAULT_ENV (char *const []){ DEFAULT_PATH, NULL } 23 | # define NULL_ENV (char *const []){ NULL } 24 | 25 | # define ARGS(...) (char *const []){ __VA_ARGS__, NULL } 26 | # define ferror(funName, errNum) fprintf(stderr, "%s: %s failed: %s\n", PROGRAM_NAME, funName, strerror(errNum)); exit(1); 27 | 28 | typedef struct s_test { 29 | 30 | char *const* commandList; 31 | char *const* envp; 32 | const char* inputFileContent; 33 | } t_test; 34 | 35 | void test(const char *testSubset, const size_t testIndex, char *const commandList[], char *const envp[], const char* inputFileContent); 36 | void runTest(const char *testSubset, const size_t testIndex, char *const commandList[], char *const envp[], const char* inputFileContent); 37 | 38 | #endif // TEST_H 39 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | S1="================================================================================" 4 | S2="--------------------------------------------------------------------------------" 5 | GREEN="\033[38;2;57;181;74m" 6 | RED="\033[38;2;222;56;43m" 7 | BLUE="\033[38;2;34;183;235m" 8 | YELLOW="\033[38;2;255;176;0m" 9 | PURPLE="\033[38;2;255;105;180m" 10 | RESET="\033[0m" 11 | 12 | INPUT=(basic error concurrency multiple_command mandatory m bonus b all a) 13 | ARG1="" 14 | ARG2="0" 15 | 16 | mkdir -p tmp/ 17 | 18 | printf "$PURPLE$S1\n\n" 19 | echo "██████╗ ██╗██████╗ ███████╗██╗ ██╗ ███╗ ███╗███████╗██████╗ ██╗ ██████╗" 20 | echo "██╔══██╗██║██╔══██╗██╔════╝╚██╗██╔╝ ████╗ ████║██╔════╝██╔══██╗██║██╔════╝" 21 | echo "██████╔╝██║██████╔╝█████╗ ╚███╔╝ ██╔████╔██║█████╗ ██║ ██║██║██║ " 22 | echo "██╔═══╝ ██║██╔═══╝ ██╔══╝ ██╔██╗ ██║╚██╔╝██║██╔══╝ ██║ ██║██║██║ " 23 | echo "██║ ██║██║ ███████╗██╔╝ ██╗ ██║ ╚═╝ ██║███████╗██████╔╝██║╚██████╗" 24 | echo "╚═╝ ╚═╝╚═╝ ╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═╝╚══════╝╚═════╝ ╚═╝ ╚═════╝" 25 | printf "\n$S1$RESET\n" 26 | 27 | if [[ -z $1 ]]; then 28 | echo "pipexMedic: launch with basic, error, concurrency, or multiple_command," 29 | echo " like this bash test.sh basics" 30 | echo " there are other aliases, as mandatory/m, bonus/b, all/a" 31 | echo " you can launch a specific test with a second argument" 32 | exit 1 33 | fi 34 | 35 | if ! [[ -z $1 ]]; then 36 | for IN in ${INPUT[@]}; do 37 | if [[ $1 == $IN ]]; then 38 | ARG1=$1 39 | fi 40 | done 41 | if [[ -z $ARG1 ]]; then 42 | echo "Invalid first argument, launch script to know usage."; rm -rf tmp/; exit 1 43 | fi 44 | fi 45 | 46 | if ! [[ -z $2 ]]; then 47 | re='^[0-9]+$' 48 | if ! [[ $2 =~ $re ]] ; then 49 | echo "Invalid second argument, launch script to know usage."; rm -rf tmp/; exit 1 50 | fi 51 | ARG2=$2 52 | fi 53 | 54 | printf "$BLUE" 55 | make -C ../ all 56 | if [[ $? -ne 0 ]]; then 57 | rm -rf tmp/; exit 1 58 | fi 59 | printf "$RESET" 60 | printf "$PURPLE$S1$RESET\n" 61 | 62 | clang -Wall -Wextra -Werror -fsanitize=address -I ./inc src/{main,test,runTest,utils}.c -o tester 63 | if [[ $? -ne 0 ]]; then 64 | rm -rf tmp/; exit 1 65 | else 66 | printf "$YELLOW""Tester ready!$RESET\n" 67 | fi 68 | printf "$PURPLE$S1$RESET\n" 69 | 70 | ./tester $ARG1 $ARG2 71 | 72 | rm -rf tmp/ tester 73 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | volatile sig_atomic_t testDone = 0; 4 | 5 | int main(int ac, char *av[]) { 6 | 7 | if (ac < 2) { 8 | ferror("input", EAGAIN); 9 | } 10 | const char *inputSubsetTest = av[1]; 11 | 12 | size_t inputSpecificTest = 0; 13 | if (ac > 2) { 14 | inputSpecificTest = atoi(av[2]); 15 | } 16 | 17 | unlink("./tester.log"); 18 | setSignal(); 19 | 20 | const t_test basicTests[] = { 21 | { ARGS("grep Hello", "wc -l"), DEFAULT_ENV, "Hello World!\n" }, 22 | { ARGS("grep Hello", "wc -l"), DEFAULT_ENV, "Hello World!\nHello World!\nHello World!\nHello World!\nHello World!\n" }, 23 | { ARGS("grep Hello", "ls -la src/"), DEFAULT_ENV, "Hello World!\n" }, 24 | { ARGS("ls -la src/", "wc -l"), DEFAULT_ENV, "Hello World!\n" }, 25 | { ARGS("grep Hello", "awk '{count++} END {print count}'"), DEFAULT_ENV, "Hello World!\nHello World!\n" }, 26 | { ARGS("grep Hello", "awk \"{count++} END {print count}\""), DEFAULT_ENV, "Hello World!\nHello World!\n" }, 27 | { ARGS("grep Hello", "awk '\"{count++} END {print count}\"'"), DEFAULT_ENV, "Hello World!\nHello World!\n" }, 28 | { ARGS("grep Hello", "awk \"'{count++} END {print count}'\""), DEFAULT_ENV, "Hello World!\nHello World!\n" } 29 | }; 30 | 31 | const t_test errorTests[] = { 32 | { ARGS("grep Hello", "wc -l"), DEFAULT_ENV, NULL }, 33 | { ARGS("grep Hello", "wc -l"), NULL_ENV, "Hello World!\n" }, 34 | { ARGS("fizzBuzz", "ls -la src/"), DEFAULT_ENV, "Hello World!\n" }, 35 | { ARGS("ls -la src/", "buzzFizz"), DEFAULT_ENV, "Hello World!\n" }, 36 | { ARGS("fizzBuzz", "wc -l"), NULL_ENV, "Hello World!\n" }, 37 | { ARGS("grep Hello", "buzzFizz"), NULL_ENV, "Hello World!\n" } 38 | }; 39 | 40 | const t_test concurrencyTests[] = { 41 | { ARGS("sleep 3", "sleep 3"), DEFAULT_ENV, "Hello World!\n" } 42 | }; 43 | 44 | const t_test multiple_commandTests[] = { 45 | { ARGS("ls -la src/", "grep .c", "wc -l"), DEFAULT_ENV, "Hello World!\n" }, 46 | { ARGS("tr -d !", "grep -v !", "sed 's/Hello/Salut/g'"), DEFAULT_ENV, "Hello World!\nHello World!\nHello World!\nHello World!\nHello World!\n" }, 47 | { ARGS("tr -d !", "grep -v !", "sed 's/Hello/Salut/g'", "grep Salut", "wc -l"), DEFAULT_ENV, "Hello World!\nHello World!\nHello World!\nHello World!\nHello World!\n" } 48 | }; 49 | 50 | if ( 51 | strcmp(inputSubsetTest, "basic") == 0 || 52 | strcmp(inputSubsetTest, "mandatory") == 0 || 53 | strcmp(inputSubsetTest, "m") == 0 || 54 | strcmp(inputSubsetTest, "all") == 0 || 55 | strcmp(inputSubsetTest, "a") == 0 56 | ) { 57 | 58 | printf(FG_YELLOW"basic tests\n"RESET); 59 | for (size_t i = 0; i < 8; i++) { 60 | if (inputSpecificTest == 0 || inputSpecificTest == i + 1) { 61 | runTest("BASIC", i + 1, basicTests[i].commandList, basicTests[i].envp, basicTests[i].inputFileContent); 62 | } 63 | } 64 | } 65 | 66 | if ( 67 | strcmp(inputSubsetTest, "error") == 0 || 68 | strcmp(inputSubsetTest, "mandatory") == 0 || 69 | strcmp(inputSubsetTest, "m") == 0 || 70 | strcmp(inputSubsetTest, "all") == 0 || 71 | strcmp(inputSubsetTest, "a") == 0 72 | ) { 73 | 74 | printf(FG_YELLOW"\nerror handling\n"RESET); 75 | for (size_t i = 0; i < 6; i++) { 76 | if (inputSpecificTest == 0 || inputSpecificTest == i + 1) { 77 | runTest("ERROR", i + 1, errorTests[i].commandList, errorTests[i].envp, errorTests[i].inputFileContent); 78 | } 79 | } 80 | } 81 | 82 | if ( 83 | strcmp(inputSubsetTest, "concurrency") == 0 || 84 | strcmp(inputSubsetTest, "mandatory") == 0 || 85 | strcmp(inputSubsetTest, "m") == 0 || 86 | strcmp(inputSubsetTest, "all") == 0 || 87 | strcmp(inputSubsetTest, "a") == 0 88 | ) { 89 | 90 | printf(FG_YELLOW"\nconcurrency launching for pipeline\n"RESET); 91 | for (size_t i = 0; i < 1; i++) { 92 | if (inputSpecificTest == 0 || inputSpecificTest == i + 1) { 93 | runTest("CONCURRENCY", i + 1, concurrencyTests[i].commandList, concurrencyTests[i].envp, concurrencyTests[i].inputFileContent); 94 | } 95 | } 96 | } 97 | 98 | if ( 99 | strcmp(inputSubsetTest, "multiple_command") == 0 || 100 | strcmp(inputSubsetTest, "bonus") == 0 || 101 | strcmp(inputSubsetTest, "b") == 0 || 102 | strcmp(inputSubsetTest, "all") == 0 || 103 | strcmp(inputSubsetTest, "a") == 0 104 | ) { 105 | 106 | printf(FG_YELLOW"\nmultiple command tests\n"RESET); 107 | for (size_t i = 0; i < 3; i++) { 108 | if (inputSpecificTest == 0 || inputSpecificTest == i + 1) { 109 | runTest("MULTIPLE COMMAND", i + 1, multiple_commandTests[i].commandList, multiple_commandTests[i].envp, multiple_commandTests[i].inputFileContent); 110 | } 111 | } 112 | } 113 | 114 | printf("\n"); 115 | return (0); 116 | } 117 | -------------------------------------------------------------------------------- /src/test.c: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | 3 | void setRedirection(const char* output, const char* error) { 4 | 5 | const int fdOutput = open(output, O_WRONLY | O_CREAT | O_TRUNC, 0644); 6 | const int fdError = open(error, O_WRONLY | O_CREAT | O_TRUNC, 0644); 7 | 8 | if (fdOutput == -1 || fdError == -1) { 9 | ferror("open", errno); 10 | } 11 | 12 | if (dup2(fdOutput, STDOUT_FILENO) == -1 || dup2(fdError, STDERR_FILENO) == -1) { 13 | ferror("dup2", errno); 14 | } 15 | } 16 | 17 | void executeCommand(const char *commandPath, char *const commandArgs[], char *const envp[], const char* output, const char* error) { 18 | 19 | const pid_t pid = fork(); 20 | 21 | if (pid == (pid_t)-1) { 22 | ferror("fork", errno); 23 | } else if (pid == (pid_t)0) { 24 | setRedirection(output, error); 25 | const int execStatus = execve(commandPath, commandArgs, envp); 26 | if (execStatus == -1) { 27 | ferror("execve", errno); 28 | } 29 | } 30 | } 31 | 32 | int getExitStatus() { 33 | 34 | int waitStatus; 35 | int exitStatus; 36 | 37 | wait(&waitStatus); 38 | if (WIFEXITED(waitStatus)) { 39 | exitStatus = WEXITSTATUS(waitStatus); 40 | } else if (WIFSIGNALED(waitStatus)) { 41 | exitStatus = WTERMSIG(waitStatus); 42 | } else { 43 | ferror("wait", EAGAIN); 44 | } 45 | return (exitStatus); 46 | } 47 | 48 | bool getFilePermission() { 49 | 50 | struct stat outputPipexStat; 51 | struct stat outputBashStat; 52 | 53 | if (stat("./outputPipex", &outputPipexStat) == -1) { 54 | ferror("stat", errno); 55 | } 56 | if (stat("./outputBash", &outputBashStat) == -1) { 57 | ferror("stat", errno); 58 | } 59 | 60 | return (outputPipexStat.st_mode == outputBashStat.st_mode); 61 | } 62 | 63 | void test(const char *testSubset, const size_t testIndex, char *const commandList[], char *const envp[], const char* inputFileContent) { 64 | 65 | const size_t nbCommand = strslen(commandList); 66 | 67 | if (inputFileContent != NULL) { 68 | FILE* fsInputFile = fopen("./input", "w"); 69 | 70 | if (fsInputFile == NULL) { 71 | ferror("fopen", errno); 72 | } 73 | fwrite(inputFileContent, strlen(inputFileContent), 1, fsInputFile); 74 | fclose(fsInputFile); 75 | } 76 | 77 | char *pipexArgs[nbCommand + 4]; 78 | 79 | pipexArgs[0] = "./pipex"; 80 | pipexArgs[1] = "input"; 81 | for (size_t i = 0; i < nbCommand; i++) { 82 | pipexArgs[i + 2] = commandList[i]; 83 | } 84 | pipexArgs[nbCommand + 2] = "outputPipex"; 85 | pipexArgs[nbCommand + 3] = NULL; 86 | 87 | char bashCommand[256] = {0}; 88 | 89 | strcat(bashCommand, "< input "); 90 | for (size_t i = 0; i < nbCommand; i++) { 91 | strcat(bashCommand, commandList[i]); 92 | if (i < nbCommand - 1) { 93 | strcat(bashCommand, " | "); 94 | } 95 | } 96 | strcat(bashCommand, " > outputBash"); 97 | 98 | char* bashArgs[] = { "bash", "-c", bashCommand, NULL }; 99 | 100 | const time_t pipexStartTime = time(NULL); 101 | executeCommand("../pipex", pipexArgs, envp, "./tmp/outputPipex.tmp", "./tmp/errorPipex.tmp"); 102 | const int pipexStatus = getExitStatus(); 103 | const time_t pipexTime = time(NULL) - pipexStartTime; 104 | 105 | const time_t bashStartTime = time(NULL); 106 | executeCommand("/usr/bin/bash", bashArgs, envp, "./tmp/outputBash.tmp", "./tmp/errorBash.tmp"); 107 | const int bashStatus = getExitStatus(); 108 | const time_t bashTime = time(NULL) - bashStartTime; 109 | 110 | system("sed -i 's/bash/pipex/g' tmp/errorBash.tmp"); 111 | 112 | bashCommand[strlen(bashCommand) - 3 - 1] = '\0'; 113 | 114 | FILE* fsOutFilePipex = fopen("./outputPipex", "r"); 115 | FILE* fsOutFileBash = fopen("./outputBash", "r"); 116 | FILE* fsStdOutPipex = fopen("./tmp/outputPipex.tmp", "r"); 117 | FILE* fsStdOutBash = fopen("./tmp/outputBash.tmp", "r"); 118 | FILE* fsStdErrPipex = fopen("./tmp/errorPipex.tmp", "r"); 119 | FILE* fsStdErrBash = fopen("./tmp/errorBash.tmp", "r"); 120 | 121 | const bool outFilePipexExists = (fsOutFilePipex == NULL) ? false : true; 122 | const bool outFileBashExists = (fsOutFileBash == NULL) ? false : true; 123 | 124 | if (fsStdOutPipex == NULL || fsStdOutBash == NULL || fsStdErrPipex == NULL || fsStdErrBash == NULL) { 125 | ferror("fopen", errno); 126 | } 127 | 128 | char outFilePipex[4096 + 1] = {0}; 129 | char outFileBash[4096 + 1] = {0}; 130 | char stdOutPipex[4096 + 1] = {0}; 131 | char stdOutBash[4096 + 1] = {0}; 132 | char stdErrPipex[4096 + 1] = {0}; 133 | char stdErrBash[4096 + 1] = {0}; 134 | 135 | if (outFilePipexExists == true) { 136 | fread(outFilePipex, 1, 4096, fsOutFilePipex); 137 | fclose(fsOutFilePipex); 138 | } 139 | if (outFileBashExists == true) { 140 | fread(outFileBash, 1, 4096, fsOutFileBash); 141 | fclose(fsOutFileBash); 142 | } 143 | fread(stdOutPipex, 1, 4096, fsStdOutPipex); 144 | fclose(fsStdOutPipex); 145 | fread(stdOutBash, 1, 4096, fsStdOutBash); 146 | fclose(fsStdOutBash); 147 | fread(stdErrPipex, 1, 4096, fsStdErrPipex); 148 | fclose(fsStdErrPipex); 149 | fread(stdErrBash, 1, 4096, fsStdErrBash); 150 | fclose(fsStdErrBash); 151 | 152 | const time_t timeDifference = pipexTime - bashTime; 153 | 154 | const bool areOutfileMatching = (!outFilePipexExists || !outFileBashExists) ? (outFilePipexExists != outFileBashExists) ? false : true : strcmp(outFilePipex, outFileBash) == 0; 155 | const bool areStdOutMatching = strcmp(stdOutPipex, stdOutBash) == 0; 156 | const bool areStdErrMatching = strcmp(stdErrPipex, stdErrBash) == 0; 157 | const bool areExitCodeMatching = pipexStatus == bashStatus; 158 | const bool isPipexUsingConcurrency = timeDifference < 2; 159 | const bool areFilePermissionsMatching = (!outFilePipexExists || !outFileBashExists) ? false : getFilePermission(); 160 | 161 | if (!areOutfileMatching || !areStdOutMatching || !areStdErrMatching || !areExitCodeMatching || !isPipexUsingConcurrency || !areFilePermissionsMatching) { 162 | 163 | FILE* fsPipexLog = fopen("tester.log", "a"); 164 | 165 | if (fsPipexLog == NULL) { 166 | ferror("fopen", errno); 167 | } 168 | 169 | char testOutput[8192 + 1]; 170 | 171 | snprintf( 172 | testOutput, 8192, 173 | "============================================================================\n\n\n" 174 | " %s TEST\n" 175 | " NUMBER %lu\n" 176 | " %s%s\n\n\n" 177 | " - Bash output file:%s" 178 | "%s" 179 | "%s" 180 | " - Pipex output file:%s" 181 | "%s" 182 | "%s" 183 | " - Bash standard stream:%s" 184 | "%s" 185 | "%s" 186 | " - Pipex standard stream:%s" 187 | "%s" 188 | "%s" 189 | "%s" 190 | " - Bash exit code value: %d\n\n" 191 | " - Pipex exit code value: %d\n\n" 192 | "%s" 193 | "%s" 194 | "\n", 195 | testSubset, 196 | testIndex, 197 | bashCommand, !envp[0] ? " (No environment)" : "", 198 | outFileBashExists ? outFileBash[0] ? "\n" : " Empty\n\n" : " None\n\n", 199 | outFileBashExists ? outFileBash[0] ? outFileBash : "" : "", 200 | outFileBashExists ? outFileBash[0] ? "\\EOF\n\n" : "" : "", 201 | outFilePipexExists ? outFilePipex[0] ? "\n" : " Empty\n\n" : " None\n\n", 202 | outFilePipexExists ? outFilePipex[0] ? outFilePipex : "" : "", 203 | outFilePipexExists ? outFilePipex[0] ? "\\EOF\n\n" : "" : "", 204 | stdErrBash[0] ? "\n" : " None\n\n", 205 | stdErrBash[0] ? stdErrBash : "", 206 | stdErrBash[0] ? "\\EOF\n\n" : "", 207 | stdErrPipex[0] ? "\n" : (stdOutPipex[0] && !areStdOutMatching) ? "\n" : " None\n\n", 208 | stdErrPipex[0] ? stdErrPipex : (stdOutPipex[0] && !areStdOutMatching) ? stdOutPipex : "", 209 | stdErrPipex[0] ? "\\EOF\n\n" : (stdOutPipex[0] && !areStdOutMatching) ? "\\EOF\n\n" : "", 210 | stdErrPipex[0] ? "" : (stdOutPipex[0] && !areStdOutMatching) ? "(Your pipex' errors are written on standard output!)\n\n" : "", 211 | bashStatus, 212 | pipexStatus, 213 | isPipexUsingConcurrency ? "" : "(Your pipex is not launching commands in parallel!)\n\n", 214 | (!outFilePipexExists || !outFileBashExists) ? "" : areFilePermissionsMatching ? "File permissions of output files match\n\n" : "File permissions of output files mismatch\n\n" 215 | ); 216 | fwrite(testOutput, strlen(testOutput), 1, fsPipexLog); 217 | fclose(fsPipexLog); 218 | printf(FG_RED"%lu. KO%s%s%s%s%s%s "RESET, testIndex, 219 | !areOutfileMatching ? " OUTPUT" : "", 220 | !areStdOutMatching ? " STDOUT" : "", 221 | !areStdErrMatching ? " STDERR" : "", 222 | !areExitCodeMatching ? " EXIT_CODE" : "", 223 | !isPipexUsingConcurrency ? " NO_CONCURRENCY" : "", 224 | !areFilePermissionsMatching ? " FILE_PERMISSIONS" : "" 225 | ); 226 | fflush(NULL); 227 | } 228 | 229 | else { 230 | printf(FG_GREEN"%lu. OK "RESET, testIndex); 231 | fflush(NULL); 232 | } 233 | } 234 | --------------------------------------------------------------------------------