shell-test.c 3.09 KB
Newer Older
Alexandru Dura's avatar
Alexandru Dura committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <fcntl.h>

#include <sys/types.h>
#include <sys/wait.h>

int create_shell_process(char **argv, int *in, int *out) {
  int in_pipe[2];
  int out_pipe[2];

  if (pipe(in_pipe) < 0)
    return -1;

  if (pipe(out_pipe) < 0)
    return -1;

  int pid = fork();
  if (pid == 0) {
    // we're in the child

    // close the reading end of the out pipe
    close(out_pipe[0]);
    // close the writing end of the in pipe
    close(in_pipe[1]);
    // redirect stdout to the writing end of the out pipe
    dup2(out_pipe[1], STDOUT_FILENO);
    // redirect stdin to the reading end of the in pipe
    dup2(in_pipe[0], STDIN_FILENO);
    // now run the executable
    char *args[3] = {argv[0], argv[1], NULL};
    execv(argv[0], args);
  }

  if (pid < 0)
    return -1;

  // we're in the parent
  close(out_pipe[1]);
  close(in_pipe[0]);


  // configure the reads to be non-blocking
  int flags = fcntl(out_pipe[0], F_GETFL);
  fcntl(out_pipe[0], F_SETFL, flags | O_NONBLOCK);

  *in = in_pipe[1];
  *out = out_pipe[0];

  return pid;
}

int send_command(const char *cmd, char *result, size_t result_size, int sleep_sec, int in, int out) {
  memset(result, 0, result_size);
  int t0 = time(NULL);
  if (write(in, cmd, strlen(cmd)) < 0) {
    return -1;
  }
  if (sleep_sec) {
    // wait for the result
    sleep(sleep_sec);
  }
  read(out, result, result_size - 1);
  int t1 = time(NULL);
  return t1 - t0;
}



#define TEST_SLEEP(cmd, expected, sleep) do {					\
  printf("================================================================================\n");\
  printf(" RUN: %s", cmd); \
  int t = send_command(cmd, result_buf, sizeof(result_buf), sleep, in, out); \
  if (t < 0) { \
    printf("Error while sending commands to the shell."); \
  } \
  if (strcmp(result_buf, expected)) {\
    ++n_fail;\
    printf("[FAIL] Expected '%s', but got '%s'.\n", expected, result_buf); \
  } else {\
    ++n_pass;\
    printf("[PASS]\n");\
  }\
  } while(0);

#define TEST(cmd, expected) \
  TEST_SLEEP(cmd, expected, 1)

int main(int argc, char **argv) {
  // create a temporary directory where to run the tests
  char tmp_dir_template[] = "test-dir-XXXXXX";
  char *tmp_dir = mkdtemp(tmp_dir_template);
  if (!chdir(tmp_dir)) {
    printf("Using test directory %s\n", tmp_dir);
  }
  char cwd[1000];
  getcwd(cwd, sizeof(cwd));

  // start the shell process
  int in, out;
  int shell_pid = create_shell_process(&argv[1], &in, &out);
  if (shell_pid < 0) {
    printf("Error launching the shell");
    exit(1);
  }

  // run the tests here
  char result_buf[1000];
  int n_pass = 0;
  int n_fail = 0;


  TEST("pwd\n", cwd);
  TEST("/bin/pwd\n", cwd);
  TEST("touch file1.txt\n", "");
  TEST("ls\n", "file1.txt");
  TEST_SLEEP("sleep 5; echo 'hello'\n", "hello", 6);
  TEST("sleep 5 & echo 'hello'\n", "hello");
  TEST("echo line1 > file1.txt\n", "");
  TEST("cat < file1.txt\n", "line1");
  TEST("cat file1.txt | wc -l\n", "1");
  TEST("cat file1.txt | cat | cat | cat | cat | wc -l\n", "1");

  close(in);

  int wstatus;
  waitpid(shell_pid, &wstatus, 0);
}