1 /*
2  test.c     UNIT tests main for HSTR shell history completion utility
3 
4  Copyright (C) 2014-2020 Martin Dvorak <martin.dvorak@mindforger.com>
5 
6  Licensed under the Apache License, Version 2.0 (the "License");
7  you may not use this file except in compliance with the License.
8  You may obtain a copy of the License at
9 
10     http://www.apache.org/licenses/LICENSE-2.0
11 
12  Unless required by applicable law or agreed to in writing, software
13  distributed under the License is distributed on an "AS IS" BASIS,
14  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  See the License for the specific language governing permissions and
16  limitations under the License.
17 */
18 
19 #define HSTR_TESTS_UNIT 1
20 
21 #include <string.h>
22 #include <regex.h>
23 #include <stdio.h>
24 #include <stdbool.h>
25 #include <getopt.h>
26 
27 // HSTR uses Unity C test framework: https://github.com/ThrowTheSwitch/Unity
28 #include "unity/src/c/unity.h"
29 
30 #include "../../src/include/hashset.h"
31 #include "../../src/include/hstr_utils.h"
32 #include "../../src/include/hstr_history.h"
33 #include "../../src/include/hstr_favorites.h"
34 #include "../../src/include/hstr.h"
35 
36 /*
37  * IMPORTANT: make sure to run TEST RUNNER GENERATOR script after any change to this file!
38  *
39  */
40 
setUp(void)41 void setUp(void)
42 {
43 }
44 
tearDown(void)45 void tearDown(void)
46 {
47 }
48 
test_args(void)49 void test_args(void)
50 {
51     const unsigned  LINELNG = 500;
52     int argc = 3;
53     char* argv[argc];
54     argv[0] = "hstr";
55     argv[1] = "-o";
56     argv[2] = "_args";
57 
58     if(argc>0) {
59         int i;
60         char line[LINELNG];
61         line[0]=0;
62         for(i=0; i<argc; i++) {
63             if((strlen(line)+strlen(argv[i])*2) > LINELNG) break;
64             printf("%d %s\n", i, argv[i]);
65             if(strstr(argv[i], " ")) {
66                 strcat(line, "\"");
67             }
68             strcat(line, argv[i]);
69             if(strstr(argv[i], " ")) {
70                 strcat(line, "\"");
71             }
72             strcat(line, " ");
73         }
74 
75         printf("#%s#\n", line);
76         TEST_ASSERT_EQUAL_STRING("hstr -o _args ", line);
77     } else {
78         TEST_FAIL_MESSAGE("There must be an argument");
79     }
80 }
81 
test_getopt(void)82 void test_getopt(void)
83 {
84     int argc = 3;
85     char* argv[argc];
86     argv[0] = "hstr";
87     argv[1] = "--create";
88     argv[2] = "_getopt";
89 
90     int c;
91     int digit_optind = 0;
92 
93     while (1) {
94         int this_option_optind = optind ? optind : 1;
95         int option_index = 0;
96         static struct option long_options[] = {
97                 {"add",     required_argument, 0,  0 },
98                 {"append",  no_argument,       0,  0 },
99                 {"delete",  required_argument, 0,  0 },
100                 {"verbose", no_argument,       0,  0 },
101                 {"create",  required_argument, 0, 'c'},
102                 {"file",    required_argument, 0,  0 },
103                 {0,         0,                 0,  0 }
104         };
105 
106         c = getopt_long(argc, argv, "abc:d:012", long_options, &option_index);
107         if (c == -1)
108             break;
109 
110 
111         switch (c) {
112         case 0:
113             printf("option %s", long_options[option_index].name);
114             if (optarg)
115                 printf(" with arg %s", optarg);
116             printf("\n");
117             break;
118 
119         case '0':
120         case '1':
121         case '2':
122             if (digit_optind != 0 && digit_optind != this_option_optind)
123                 printf("digits occur in two different argv-elements.\n");
124             digit_optind = this_option_optind;
125             printf("option %c\n", c);
126             break;
127 
128         case 'a':
129             printf("option a\n");
130             break;
131 
132         case 'b':
133             printf("option b\n");
134             break;
135 
136         case 'c':
137             printf("option c with value '%s'\n", optarg);
138             break;
139 
140         case 'd':
141             printf("option d with value '%s'\n", optarg);
142             break;
143 
144         case '?':
145 
146             break;
147 
148         default:
149             printf("?? getopt returned character code 0%o ??\n", c);
150         }
151 
152         TEST_ASSERT_EQUAL('c', c);
153     }
154 
155     if(optind < argc) {
156         printf("non-option ARGV-elements: ");
157         while (optind < argc) {
158             printf("%s ", argv[optind++]);
159         }
160         printf("\n");
161         TEST_FAIL_MESSAGE("Thist unit test is supposed to succeed with 'c' option");
162     }
163 }
164 
test_locate_char_in_string_overflow(void)165 void test_locate_char_in_string_overflow(void)
166 {
167     TEST_ASSERT_EQUAL_STRING(0, strchr("a\nb",1));
168 
169     printf("%s\n",strchr("a\nb",98));
170     TEST_ASSERT_EQUAL_STRING("b", strchr("a\nb",98));
171 
172     printf("%s\n",strchr("a\nb",10));
173     TEST_ASSERT_EQUAL_STRING("\nb", strchr("a\nb",10));
174 }
175 
test_favorites(void)176 void test_favorites(void)
177 {
178     FavoriteItems favoriteItems;
179 
180     favorites_init(&favoriteItems);
181     favorites_get(&favoriteItems);
182 
183     char* favorite = "UNIT-TEST-ENTRY";
184     unsigned count = favoriteItems.count;
185 
186     TEST_ASSERT_EQUAL(count, favoriteItems.count);
187     favorites_add(&favoriteItems, favorite);
188     TEST_ASSERT_EQUAL(count+1, favoriteItems.count);
189 
190     favorites_choose(&favoriteItems, favorite);
191     TEST_ASSERT_EQUAL_STRING(favorite, favoriteItems.items[0]);
192 
193     favorites_remove(&favoriteItems, favorite);
194     TEST_ASSERT_EQUAL(count, favoriteItems.count);
195 
196     favorites_choose(&favoriteItems, favorite);
197     TEST_ASSERT_NOT_EQUAL(favorite, favoriteItems.items[0]);
198 }
199 
test_hashset_blacklist()200 void test_hashset_blacklist()
201 {
202     const char* commandBlacklist[] = { "a","b","c","d","e" };
203     HashSet blacklist;
204     int i;
205     hashset_init(&blacklist);
206     for (i = 0; i < 5; i++) {
207         TEST_ASSERT_TRUE(hashset_add(&blacklist, commandBlacklist[i]));
208     }
209     for (i = 0; i < 5; i++) {
210         printf("match %d\n", hashset_contains(&blacklist, hstr_strdup(commandBlacklist[i])));
211         TEST_ASSERT_TRUE(hashset_contains(&blacklist, hstr_strdup(commandBlacklist[i])));
212     }
213 }
214 
test_hashset_get_keys()215 void test_hashset_get_keys()
216 {
217     const char* commandBlacklist[] = { "a","b","c","d","e" };
218     HashSet blacklist;
219     int i;
220     hashset_init(&blacklist);
221     for (i = 0; i < 5; i++) {
222         TEST_ASSERT_TRUE(hashset_add(&blacklist, commandBlacklist[i]));
223     }
224 
225     char **keys=hashset_keys(&blacklist);
226     if(keys) {
227         for(i=0; i<hashset_size(&blacklist); i++) {
228             printf("Key: %s\n", keys[i]);
229             TEST_ASSERT_NOT_EQUAL(NULL, keys[i]);
230         }
231     } else {
232         TEST_FAIL_MESSAGE("Inserted keys are missing in hashset");
233     }
234 }
235 
test_regexp(void)236 void test_regexp(void)
237 {
238     unsigned REGEXP_MATCH_BUFFER_SIZE = 10;
239 
240     bool caseSensitive=false;
241 
242     char *regexp="^b";
243     char *text="This is a command that I want to match: go.sh there";
244 
245     regex_t precompiled;
246     int compilationFlags=(caseSensitive?0:REG_ICASE);
247     printf("Regular expressions matching:\n  '%s'\n  '%s'",text,regexp);
248     int compilationStatus=regcomp(&precompiled, regexp, compilationFlags);
249     printf("\nCompilation: %d",compilationStatus);
250     TEST_ASSERT_FALSE(compilationStatus);
251 
252     int matches=REGEXP_MATCH_BUFFER_SIZE;
253     regmatch_t matchPtr[REGEXP_MATCH_BUFFER_SIZE];
254     int matchingFlags=0;
255     int matchingStatus=regexec(&precompiled, text, matches, matchPtr, matchingFlags);
256     printf("\nMatching (status/matches): %s %d",(!matchingStatus?"match":"no-match"), matches);
257     TEST_ASSERT_EQUAL(10, matches);
258 
259     if(!matchingStatus) {
260         unsigned i;
261         for(i=0; i<REGEXP_MATCH_BUFFER_SIZE; i++) {
262             printf("\n  %d %d",matchPtr[i].rm_so, matchPtr[i].rm_eo);
263             if(matchPtr[i].rm_so != -1) {
264                 printf("\n* %d %d",matchPtr[i].rm_so,matchPtr[i].rm_eo);
265             }
266         }
267     } else {
268         char message[100];
269         regerror(matchingStatus, &precompiled, message, 100);
270         printf("\nRegexp failed with %s\n", message);
271     }
272 
273     printf("\n");
274 }
275 
test_help_long(void)276 void test_help_long(void)
277 {
278     TEST_IGNORE_MESSAGE("Tests exits the program");
279 
280     char* ARG_HSTR = "hstr";
281     char* ARG_HELP = "--help";
282     int argc = 1+1;
283     char* argv[argc];
284     argv[0] = ARG_HSTR;
285     argv[1] = ARG_HELP;
286     int s;
287 
288     s = hstr_main(argc, argv);
289     TEST_ASSERT_FALSE(s);
290 }
291 
test_help_short(void)292 void test_help_short(void)
293 {
294     TEST_IGNORE_MESSAGE("Tests exits the program");
295 
296     char* ARG_HSTR = "hstr";
297     char* ARG_H = "-h";
298     int argc = 1+1;
299     char* argv[argc];
300     argv[0] = ARG_HSTR;
301     argv[1] = ARG_H;
302     int s;
303 
304     s = hstr_main(argc, argv);
305     TEST_ASSERT_FALSE(s);
306 }
307 
test_string_elide()308 void test_string_elide()
309 {
310     char buffer[1000];
311 
312     // do nothing - string fits to screen
313     hstr_strelide(buffer, "0123456789", 20);
314     printf("%s\n", buffer);
315     TEST_ASSERT_EQUAL_STRING("0123456789", buffer);
316 
317     // do nothing - string too short
318     hstr_strelide(buffer, "012", 1);
319     printf("%s\n", buffer);
320     TEST_ASSERT_EQUAL_STRING("012", buffer);
321 
322     hstr_strelide(buffer, "0123456789", 6);
323     printf("%s\n", buffer);
324     TEST_ASSERT_EQUAL_STRING("01...9", buffer);
325 
326 
327     hstr_strelide(buffer, "0123456789", 10);
328     TEST_ASSERT_EQUAL_STRING("0123456789", buffer);
329     printf("%s\n", buffer);
330 
331     hstr_strelide(buffer, "0123456789", 9);
332     TEST_ASSERT_EQUAL_STRING("012...789", buffer);
333     printf("%s\n", buffer);
334 
335     hstr_strelide(buffer, "0123456789", 8);
336     TEST_ASSERT_EQUAL_STRING("012...89", buffer);
337     printf("%s\n", buffer);
338 }
339 
test_parse_history_line()340 void test_parse_history_line()
341 {
342     TEST_ASSERT_EQUAL(NULL, parse_history_line(NULL));
343 
344     TEST_ASSERT_EQUAL_STRING("ls", parse_history_line("ls"));
345     TEST_ASSERT_EQUAL_STRING(":::", parse_history_line(":::"));
346 
347     TEST_ASSERT_EQUAL_STRING(":vspman epoll_ctl", parse_history_line(": 1592444398:0;:vspman epoll_ctl"));
348     TEST_ASSERT_EQUAL_STRING("scan-view work/scans/2020-07-30-114451-3063582-1", parse_history_line(": 1596135828:6109;scan-view work/scans/2020-07-30-114451-3063582-1"));
349     TEST_ASSERT_EQUAL_STRING(":wq", parse_history_line(": 1592444398:0;:wq"));
350     TEST_ASSERT_EQUAL_STRING(":wq", parse_history_line(":wq"));
351 
352     TEST_ASSERT_EQUAL_STRING(": 159244439:0;:wq", parse_history_line(": 159244439:0;:wq"));
353     TEST_ASSERT_EQUAL_STRING(": 1592444398:;:wq", parse_history_line(": 1592444398:;:wq"));
354     TEST_ASSERT_EQUAL_STRING(": 1592444398:0:wq", parse_history_line(": 1592444398:0:wq"));
355     TEST_ASSERT_EQUAL_STRING(":1592444398:0;:vspman epoll_ctl", parse_history_line(":1592444398:0;:vspman epoll_ctl"));
356 }
357