Merge pull request #3339 from jkohen/patch-1
[collectd.git] / src / utils / proc_pids / proc_pids_test.c
1 // clang-format off
2 /*
3  * Explicit order is required or _FILE_OFFSET_BITS will have definition mismatches on Solaris
4  * See Github Issue #3193 for details
5  */
6 #include "utils/proc_pids/proc_pids.c" /* sic */
7 #include "testing.h"
8 // clang-format on
9 #include <sys/stat.h>
10
11 /***************************************************************************
12  * helper functions
13  */
14
15 typedef struct stub_proc_pid {
16   proc_comm_t comm;
17   pid_t pid;
18 } stub_proc_pid_t;
19
20 static const char *proc_fs = "/tmp/procfs_stub";
21
22 /*
23  * NAME
24  *   stub_procfs_setup
25  *
26  * DESCRIPTION
27  *   Prepares testing environment by creating temporary
28  *   PID/comm file structure.
29  *
30  * PARAMETERS
31  *   `proc_pids_array'          Array of stub_proc_pid_t structs. Represents
32  *                              which PIDs should hold given process name.
33  *   `proc_pids_array_length'   Element count of input array.
34  *
35  * RETURN VALUE
36  *   0 on success.
37  *   -1 on base dir creation error.
38  *   -2 on comm file creation error.
39  *   -3 on comm file write error.
40  */
41 int stub_procfs_setup(const stub_proc_pid_t *proc_pids_array,
42                       const size_t proc_pids_array_length) {
43   if (mkdir(proc_fs, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0)
44     return -1;
45   char path[256];
46
47   for (size_t i = 0; i < proc_pids_array_length; ++i) {
48     memset(path, 0, sizeof(path));
49     snprintf(path, STATIC_ARRAY_SIZE(path), "%s/%d", proc_fs,
50              proc_pids_array[i].pid);
51     mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
52     strncat(path, "/comm", STATIC_ARRAY_SIZE(path) - strlen(path) - 1);
53
54     FILE *fp = fopen(path, "w");
55     if (!fp)
56       return -2;
57
58     size_t slen = strlen(proc_pids_array[i].comm);
59     size_t wlen = fwrite(proc_pids_array[i].comm, sizeof(char), slen, fp);
60     fclose(fp);
61
62     if (slen != wlen)
63       return -3;
64   }
65   return 0;
66 }
67
68 /*
69  * NAME
70  *   stub_procfs_teardown
71  *
72  * DESCRIPTION
73  *   Clears testing environment: removes stub proc files.
74  *   NOTE - This function could be implemented by usage of nftw, but this
75  *   would require #define _XOPEN_SOURCE 500, which
76  *   messes up intel_rdt includes.
77  *
78  * RETURN VALUE
79  *   system command result
80  */
81 int stub_procfs_teardown() {
82   char cmd[256];
83   sstrncpy(cmd, "rm -rf ", STATIC_ARRAY_SIZE(cmd));
84   strncat(cmd, proc_fs, STATIC_ARRAY_SIZE(cmd) - strlen(cmd) - 1);
85   return system(cmd);
86 }
87
88 /* Max PID value. More info:
89  * http://web.archive.org/web/20111209081734/http://research.cs.wisc.edu/condor/condorg/linux_scalability.html
90  */
91 #define MAX_PID 4194304
92 #define MAX_PID_STR "4194304"
93
94 /***************************************************************************
95  * tests
96  */
97 DEF_TEST(proc_pids_init__on_nullptr) {
98   /* setup */
99   const char *procs_names_array[] = {"proc1", "proc2", "proc3"};
100   const size_t procs_names_array_size = STATIC_ARRAY_SIZE(procs_names_array);
101   proc_pids_t **proc_pids_array = NULL;
102
103   /* check */
104   int result = proc_pids_init(procs_names_array, procs_names_array_size,
105                               &proc_pids_array);
106   EXPECT_EQ_INT(0, result);
107   for (size_t i = 0; i < procs_names_array_size; ++i)
108     EXPECT_EQ_STR(procs_names_array[i], proc_pids_array[i]->process_name);
109
110   /* cleanup */
111   proc_pids_free(proc_pids_array, procs_names_array_size);
112   return 0;
113 }
114
115 DEF_TEST(pid_list_add_pid__empty_list) {
116   /* setup */
117   pids_list_t *proc_pids_instance = calloc(1, sizeof(*proc_pids_instance));
118   pid_t pid = 1234;
119
120   /* check */
121   pids_list_add_pid(proc_pids_instance, pid);
122   EXPECT_EQ_INT(pid, proc_pids_instance->pids[0]);
123
124   /* cleanup */
125   pids_list_free(proc_pids_instance);
126   return 0;
127 }
128
129 DEF_TEST(pid_list_add_pid__non_empty_list) {
130   /* setup */
131   pids_list_t *proc_pids_instance = calloc(1, sizeof(*proc_pids_instance));
132   pid_t pids[] = {1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007};
133
134   /* check */
135   for (size_t i = 0; i < STATIC_ARRAY_SIZE(pids); ++i)
136     pids_list_add_pid(proc_pids_instance, pids[i]);
137
138   for (size_t i = 0; i < STATIC_ARRAY_SIZE(pids); ++i) {
139     EXPECT_EQ_INT(pids[i], proc_pids_instance->pids[i]);
140   }
141
142   /* cleanup */
143   pids_list_free(proc_pids_instance);
144   return 0;
145 }
146
147 DEF_TEST(pids_list_add_pids_list__non_empty_lists) {
148   /* setup */
149   pid_t pids_array_1[] = {1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007};
150   pid_t pids_array_2[] = {2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007};
151   pids_list_t *pids_list_1 = calloc(1, sizeof(*pids_list_1));
152   pids_list_t *pids_list_2 = calloc(1, sizeof(*pids_list_2));
153   for (size_t i = 0; i < STATIC_ARRAY_SIZE(pids_array_1); ++i) {
154     pids_list_add_pid(pids_list_1, pids_array_1[i]);
155     pids_list_add_pid(pids_list_2, pids_array_2[i]);
156   }
157
158   /* check */
159   int result = pids_list_add_list(pids_list_1, pids_list_2);
160   EXPECT_EQ_INT(0, result);
161   EXPECT_EQ_INT(STATIC_ARRAY_SIZE(pids_array_2) +
162                     STATIC_ARRAY_SIZE(pids_array_1),
163                 pids_list_1->size);
164
165   for (size_t i = 0; i < STATIC_ARRAY_SIZE(pids_array_1); ++i) {
166     EXPECT_EQ_INT(1, pids_list_contains_pid(pids_list_1, pids_array_1[i]));
167     EXPECT_EQ_INT(1, pids_list_contains_pid(pids_list_1, pids_array_2[i]));
168   }
169
170   /* setup */
171   pids_list_free(pids_list_1);
172   pids_list_free(pids_list_2);
173   return 0;
174 }
175
176 DEF_TEST(pids_list_add_pids_list__add_to_empty) {
177   /* setup */
178   pid_t pids_array[] = {2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007};
179   pids_list_t *pids_list_1 = calloc(1, sizeof(*pids_list_1));
180   pids_list_t *pids_list_2 = calloc(1, sizeof(*pids_list_2));
181   for (size_t i = 0; i < STATIC_ARRAY_SIZE(pids_array); ++i)
182     pids_list_add_pid(pids_list_2, pids_array[i]);
183
184   /* check */
185   int result = pids_list_add_list(pids_list_1, pids_list_2);
186   EXPECT_EQ_INT(0, result);
187   EXPECT_EQ_INT(STATIC_ARRAY_SIZE(pids_array), pids_list_1->size);
188
189   for (size_t i = 0; i < STATIC_ARRAY_SIZE(pids_array); ++i)
190     EXPECT_EQ_INT(1, pids_list_contains_pid(pids_list_1, pids_array[i]));
191
192   /* setup */
193   pids_list_free(pids_list_1);
194   pids_list_free(pids_list_2);
195   return 0;
196 }
197
198 DEF_TEST(get_pid_number__valid_dir) {
199   /* setup */
200   struct dirent d;
201   sstrncpy(d.d_name, MAX_PID_STR, STATIC_ARRAY_SIZE(d.d_name));
202   d.d_type = DT_DIR;
203   pid_t pid = 0;
204
205   /* check */
206   int pid_conversion = get_pid_number(&d, &pid);
207
208   EXPECT_EQ_INT(0, pid_conversion);
209   EXPECT_EQ_INT(MAX_PID, pid);
210
211   /* cleanup */
212   return 0;
213 }
214
215 DEF_TEST(get_pid_number__invalid_dir_name) {
216   /* setup */
217   struct dirent d;
218   sstrncpy(d.d_name, "invalid", STATIC_ARRAY_SIZE(d.d_name));
219   d.d_type = DT_DIR;
220   pid_t pid = 0;
221
222   /* check */
223   int pid_conversion = get_pid_number(&d, &pid);
224
225   EXPECT_EQ_INT(-1, pid_conversion);
226   EXPECT_EQ_INT(0, pid);
227
228   /* cleanup */
229   return 0;
230 }
231
232 DEF_TEST(read_proc_name__valid_name) {
233   /* setup */
234   stub_proc_pid_t pp_stubs[] = {{"proc1", MAX_PID}};
235   stub_procfs_setup(pp_stubs, STATIC_ARRAY_SIZE(pp_stubs));
236   struct dirent d;
237   sstrncpy(d.d_name, MAX_PID_STR, STATIC_ARRAY_SIZE(d.d_name));
238   d.d_type = DT_DIR;
239
240   /* check */
241   proc_comm_t comm;
242   int read_result = read_proc_name(proc_fs, &d, comm, STATIC_ARRAY_SIZE(comm));
243
244   EXPECT_EQ_INT(strlen(pp_stubs[0].comm), read_result);
245   EXPECT_EQ_STR(pp_stubs[0].comm, comm);
246
247   /* cleanup */
248   stub_procfs_teardown();
249   return 0;
250 }
251
252 DEF_TEST(read_proc_name__invalid_name) {
253   /* setup */
254   struct dirent d;
255   sstrncpy(d.d_name, MAX_PID_STR, STATIC_ARRAY_SIZE(d.d_name));
256   d.d_type = DT_DIR;
257
258   /* check */
259   proc_comm_t comm;
260   int read_result = read_proc_name(proc_fs, &d, comm, STATIC_ARRAY_SIZE(comm));
261
262   EXPECT_EQ_INT(-1, read_result);
263
264   /* cleanup */
265   return 0;
266 }
267
268 DEF_TEST(proc_pids_update__one_proc_many_pid) {
269   /* setup */
270   const char *proc_names[] = {"proc1"};
271   stub_proc_pid_t pp_stubs[] = {{"proc1", 1007},
272                                 {"proc1", 1008},
273                                 {"proc1", 1009},
274                                 {"proc2", 1010},
275                                 {"proc3", 1011}};
276   proc_pids_t **proc_pids = NULL;
277   int result;
278   stub_procfs_setup(pp_stubs, STATIC_ARRAY_SIZE(pp_stubs));
279
280   result =
281       proc_pids_init(proc_names, STATIC_ARRAY_SIZE(proc_names), &proc_pids);
282   EXPECT_EQ_INT(0, result);
283
284   /* check */
285   result = proc_pids_update(proc_fs, proc_pids, STATIC_ARRAY_SIZE(proc_names));
286   EXPECT_EQ_INT(0, result);
287
288   /* proc name check */
289   EXPECT_EQ_STR(proc_names[0], proc_pids[0]->process_name);
290
291   for (size_t i = 0; i < STATIC_ARRAY_SIZE(pp_stubs); ++i) {
292     if (0 == strcmp(pp_stubs[i].comm, proc_names[0]))
293       /* check if proc struct has correct pids */
294       EXPECT_EQ_INT(pids_list_contains_pid(proc_pids[0]->curr, pp_stubs[i].pid),
295                     1);
296     else
297       /* check if proc struct has no incorrect pids */
298       EXPECT_EQ_INT(pids_list_contains_pid(proc_pids[0]->curr, pp_stubs[i].pid),
299                     0);
300   }
301
302   /* cleanup */
303   proc_pids_free(proc_pids, STATIC_ARRAY_SIZE(proc_names));
304   stub_procfs_teardown();
305   return 0;
306 }
307
308 DEF_TEST(proc_pids_update__many_proc_many_pid) {
309   /* setup */
310   const char *proc_names[] = {"proc1", "proc2", "proc3"};
311   stub_proc_pid_t pp_stubs[] = {
312       {"proc1", 1007}, {"proc1", 1008}, {"proc1", 1009}, {"proc2", 2007},
313       {"proc2", 2008}, {"proc2", 2009}, {"proc3", 3007}, {"proc3", 3008},
314       {"proc3", 3009}, {"proc4", 4007}, {"proc4", 4008}, {"proc4", 4009},
315       {"proc5", 5007}, {"proc5", 5008}, {"proc5", 5009}};
316   proc_pids_t **proc_pids = NULL;
317   int result;
318   stub_procfs_setup(pp_stubs, STATIC_ARRAY_SIZE(pp_stubs));
319
320   result =
321       proc_pids_init(proc_names, STATIC_ARRAY_SIZE(proc_names), &proc_pids);
322   EXPECT_EQ_INT(0, result);
323
324   /* check */
325   result = proc_pids_update(proc_fs, proc_pids, STATIC_ARRAY_SIZE(proc_names));
326   EXPECT_EQ_INT(0, result);
327
328   for (size_t i = 0; i < STATIC_ARRAY_SIZE(proc_names); ++i) {
329
330     /* proc name check */
331     EXPECT_EQ_STR(proc_names[i], proc_pids[i]->process_name);
332
333     for (size_t j = 0; j < STATIC_ARRAY_SIZE(pp_stubs); ++j) {
334       if (0 == strcmp(pp_stubs[j].comm, proc_names[i]))
335         /* check if proc struct has correct pids */
336         EXPECT_EQ_INT(
337             pids_list_contains_pid(proc_pids[i]->curr, pp_stubs[j].pid), 1);
338       else
339         /* check if proc struct has no incorrect pids */
340         EXPECT_EQ_INT(
341             pids_list_contains_pid(proc_pids[i]->curr, pp_stubs[j].pid), 0);
342     }
343   }
344
345   /* cleanup */
346   proc_pids_free(proc_pids, STATIC_ARRAY_SIZE(proc_names));
347   stub_procfs_teardown();
348   return 0;
349 }
350
351 DEF_TEST(pids_list_diff__all_changed) {
352   /* setup */
353   pid_t pids_array_before[] = {1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007};
354   pid_t pids_array_after[] = {2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007};
355   proc_pids_t proc_pids;
356   pids_list_t curr;
357   pids_list_t prev;
358
359   prev.pids = pids_array_before;
360   prev.size = STATIC_ARRAY_SIZE(pids_array_before);
361   prev.allocated = prev.size;
362   curr.pids = pids_array_after;
363   curr.size = STATIC_ARRAY_SIZE(pids_array_after);
364   curr.allocated = curr.size;
365   proc_pids.curr = &curr;
366   proc_pids.prev = &prev;
367
368   pids_list_t *new_pids = calloc(1, sizeof(*new_pids));
369   pids_list_t *lost_pids = calloc(1, sizeof(*lost_pids));
370
371   /* check */
372   int result = pids_list_diff(&proc_pids, new_pids, lost_pids);
373   EXPECT_EQ_INT(0, result);
374   EXPECT_EQ_INT(STATIC_ARRAY_SIZE(pids_array_before), lost_pids->size);
375   EXPECT_EQ_INT(STATIC_ARRAY_SIZE(pids_array_after), new_pids->size);
376
377   for (size_t i = 0; i < STATIC_ARRAY_SIZE(pids_array_before); ++i) {
378     EXPECT_EQ_INT(1, pids_list_contains_pid(new_pids, pids_array_after[i]));
379     EXPECT_EQ_INT(1, pids_list_contains_pid(lost_pids, pids_array_before[i]));
380   }
381
382   /* cleanup */
383   pids_list_free(new_pids);
384   pids_list_free(lost_pids);
385
386   return 0;
387 }
388
389 DEF_TEST(pids_list_diff__nothing_changed) {
390   /* setup */
391   pid_t pids_array_before[] = {1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007};
392   proc_pids_t proc_pids;
393   pids_list_t curr;
394   pids_list_t prev;
395
396   prev.pids = pids_array_before;
397   prev.size = STATIC_ARRAY_SIZE(pids_array_before);
398   prev.allocated = prev.size;
399   curr.pids = pids_array_before;
400   curr.size = STATIC_ARRAY_SIZE(pids_array_before);
401   curr.allocated = curr.size;
402   proc_pids.curr = &curr;
403   proc_pids.prev = &prev;
404
405   pids_list_t *new_pids = calloc(1, sizeof(*new_pids));
406   pids_list_t *lost_pids = calloc(1, sizeof(*lost_pids));
407
408   /* check */
409   int result = pids_list_diff(&proc_pids, new_pids, lost_pids);
410   EXPECT_EQ_INT(0, result);
411   EXPECT_EQ_INT(0, lost_pids->size);
412   EXPECT_EQ_INT(0, new_pids->size);
413
414   /* cleanup */
415   pids_list_free(lost_pids);
416   pids_list_free(new_pids);
417
418   return 0;
419 }
420
421 DEF_TEST(pids_list_diff__one_added) {
422   /* setup */
423   pid_t pids_array_before[] = {1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007};
424   pid_t pids_array_after[] = {1000, 1001, 1002, 1003, 1004,
425                               1005, 1006, 1007, 1008};
426   proc_pids_t proc_pids;
427   pids_list_t curr;
428   pids_list_t prev;
429
430   prev.pids = pids_array_before;
431   prev.size = STATIC_ARRAY_SIZE(pids_array_before);
432   prev.allocated = prev.size;
433   curr.pids = pids_array_after;
434   curr.size = STATIC_ARRAY_SIZE(pids_array_after);
435   curr.allocated = curr.size;
436   proc_pids.curr = &curr;
437   proc_pids.prev = &prev;
438
439   pids_list_t *new_pids = calloc(1, sizeof(*new_pids));
440   pids_list_t *lost_pids = calloc(1, sizeof(*lost_pids));
441
442   /* check */
443   int result = pids_list_diff(&proc_pids, new_pids, lost_pids);
444   EXPECT_EQ_INT(0, result);
445   EXPECT_EQ_INT(0, lost_pids->size);
446   EXPECT_EQ_INT(1, new_pids->size);
447   EXPECT_EQ_INT(1008, new_pids->pids[0]);
448
449   /* cleanup */
450   pids_list_free(lost_pids);
451   pids_list_free(new_pids);
452
453   return 0;
454 }
455
456 DEF_TEST(pids_list_diff__one_removed) {
457   /* setup */
458   pid_t pids_array_before[] = {1000, 1001, 1002, 1003, 1004,
459                                1005, 1006, 1007, 1008};
460   pid_t pids_array_after[] = {1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007};
461
462   proc_pids_t proc_pids;
463   pids_list_t curr;
464   pids_list_t prev;
465
466   prev.pids = pids_array_before;
467   prev.size = STATIC_ARRAY_SIZE(pids_array_before);
468   prev.allocated = prev.size;
469   curr.pids = pids_array_after;
470   curr.size = STATIC_ARRAY_SIZE(pids_array_after);
471   curr.allocated = curr.size;
472   proc_pids.curr = &curr;
473   proc_pids.prev = &prev;
474
475   pids_list_t *new_pids = calloc(1, sizeof(*new_pids));
476   pids_list_t *lost_pids = calloc(1, sizeof(*lost_pids));
477
478   /* check */
479   int result = pids_list_diff(&proc_pids, new_pids, lost_pids);
480   EXPECT_EQ_INT(0, result);
481   EXPECT_EQ_INT(0, new_pids->size);
482   EXPECT_EQ_INT(1, lost_pids->size);
483   EXPECT_EQ_INT(1008, lost_pids->pids[0]);
484
485   /* cleanup */
486   pids_list_free(lost_pids);
487   pids_list_free(new_pids);
488
489   return 0;
490 }
491
492 int main(void) {
493   stub_procfs_teardown();
494   RUN_TEST(proc_pids_init__on_nullptr);
495   RUN_TEST(pid_list_add_pid__empty_list);
496   RUN_TEST(pid_list_add_pid__non_empty_list);
497   RUN_TEST(pids_list_add_pids_list__non_empty_lists);
498   RUN_TEST(pids_list_add_pids_list__add_to_empty);
499   RUN_TEST(get_pid_number__valid_dir);
500   RUN_TEST(get_pid_number__invalid_dir_name);
501   RUN_TEST(read_proc_name__valid_name);
502   RUN_TEST(read_proc_name__invalid_name);
503   RUN_TEST(proc_pids_update__one_proc_many_pid);
504   RUN_TEST(proc_pids_update__many_proc_many_pid);
505   RUN_TEST(pids_list_diff__all_changed);
506   RUN_TEST(pids_list_diff__nothing_changed);
507   RUN_TEST(pids_list_diff__one_added);
508   RUN_TEST(pids_list_diff__one_removed);
509   stub_procfs_teardown();
510   END_TEST;
511 }