1 /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  *     Copyright 2015 Couchbase, Inc
4  *
5  *   Licensed under the Apache License, Version 2.0 (the "License");
6  *   you may not use this file except in compliance with the License.
7  *   You may obtain a copy of the License at
8  *
9  *       http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *   Unless required by applicable law or agreed to in writing, software
12  *   distributed under the License is distributed on an "AS IS" BASIS,
13  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *   See the License for the specific language governing permissions and
15  *   limitations under the License.
16  */
17 
18 /*
19  * Unit tests for util.c
20  */
21 
22 #include <platform/cb_malloc.h>
23 #include <platform/platform.h>
24 
25 #include <memcached/util.h>
26 #include <memcached/config_parser.h>
27 #include "string_utilities.h"
28 
29 #include <gtest/gtest.h>
30 #include <gmock/gmock.h>
31 
32 #define TMP_TEMPLATE "testapp_tmp_file.XXXXXXX"
33 
TEST(StringTest, safe_strtoul)34 TEST(StringTest, safe_strtoul) {
35     uint32_t val;
36     EXPECT_TRUE(safe_strtoul("123", val));
37     EXPECT_EQ(123u, val);
38     EXPECT_TRUE(safe_strtoul("+123", val));
39     EXPECT_EQ(123u, val);
40     EXPECT_FALSE(safe_strtoul("", val));  /* empty */
41     EXPECT_FALSE(safe_strtoul("123BOGUS", val));  /* non-numeric */
42     /* Not sure what it does, but this works with ICC :/
43        EXPECT_FALSE(safe_strtoul("92837498237498237498029383", val)); // out of range
44     */
45 
46     /* extremes: */
47     EXPECT_TRUE(safe_strtoul("4294967295", val)); /* 2**32 - 1 */
48     EXPECT_EQ(4294967295L, val);
49     /* This actually works on 64-bit ubuntu
50        EXPECT_FALSE(safe_strtoul("4294967296", val)); 2**32
51     */
52     EXPECT_FALSE(safe_strtoul("-1", val));  /* negative */
53 }
54 
55 
TEST(StringTest, safe_strtoull)56 TEST(StringTest, safe_strtoull) {
57     uint64_t val;
58     uint64_t exp = -1;
59     EXPECT_TRUE(safe_strtoull("123", val));
60     EXPECT_EQ(123u, val);
61     EXPECT_TRUE(safe_strtoull("+123", val));
62     EXPECT_EQ(123u, val);
63     EXPECT_FALSE(safe_strtoull("", val));  /* empty */
64     EXPECT_FALSE(safe_strtoull("123BOGUS", val));  /* non-numeric */
65     EXPECT_FALSE(safe_strtoull("92837498237498237498029383", val)); /* out of range */
66 
67     /* extremes: */
68     EXPECT_TRUE(safe_strtoull("18446744073709551615", val)); /* 2**64 - 1 */
69     EXPECT_EQ(exp, val);
70     EXPECT_FALSE(safe_strtoull("18446744073709551616", val)); /* 2**64 */
71     EXPECT_FALSE(safe_strtoull("-1", val));  /* negative */
72 }
73 
TEST(StringTest, safe_strtoll)74 TEST(StringTest, safe_strtoll) {
75     int64_t val;
76     EXPECT_TRUE(safe_strtoll("123", val));
77     EXPECT_EQ(123, val);
78     EXPECT_TRUE(safe_strtoll("+123", val));
79     EXPECT_EQ(123, val);
80     EXPECT_TRUE(safe_strtoll("-123", val));
81     EXPECT_EQ(-123, val);
82     EXPECT_FALSE(safe_strtoll("", val));  /* empty */
83     EXPECT_FALSE(safe_strtoll("123BOGUS", val));  /* non-numeric */
84     EXPECT_FALSE(safe_strtoll("92837498237498237498029383", val)); /* out of range */
85 
86     /* extremes: */
87     EXPECT_FALSE(safe_strtoll("18446744073709551615", val)); /* 2**64 - 1 */
88     EXPECT_TRUE(safe_strtoll("9223372036854775807", val)); /* 2**63 - 1 */
89 
90     EXPECT_EQ(std::numeric_limits<int64_t>::max(),
91               val); /* 9223372036854775807LL); */
92     /*
93       EXPECT_EQ(safe_strtoll("-9223372036854775808", val)); // -2**63
94       EXPECT_EQ(val, -9223372036854775808LL);
95     */
96     EXPECT_FALSE(safe_strtoll("-9223372036854775809", val)); /* -2**63 - 1 */
97 
98     /* We'll allow space to terminate the string.  And leading space. */
99     EXPECT_TRUE(safe_strtoll(" 123 foo", val));
100     EXPECT_EQ(123, val);
101 }
102 
TEST(StringTest, safe_strtol)103 TEST(StringTest, safe_strtol) {
104     int32_t val;
105     EXPECT_TRUE(safe_strtol("123", val));
106     EXPECT_EQ(123, val);
107     EXPECT_TRUE(safe_strtol("+123", val));
108     EXPECT_EQ(123, val);
109     EXPECT_TRUE(safe_strtol("-123", val));
110     EXPECT_EQ(-123, val);
111     EXPECT_FALSE(safe_strtol("", val));  /* empty */
112     EXPECT_FALSE(safe_strtol("123BOGUS", val));  /* non-numeric */
113     EXPECT_FALSE(safe_strtol("92837498237498237498029383", val)); /* out of range */
114 
115     /* extremes: */
116     /* This actually works on 64-bit ubuntu
117        EXPECT_FALSE(safe_strtol("2147483648", val)); // (expt 2.0 31.0)
118     */
119     EXPECT_TRUE(safe_strtol("2147483647", val)); /* (- (expt 2.0 31) 1) */
120     EXPECT_EQ(2147483647L, val);
121     /* This actually works on 64-bit ubuntu
122        EXPECT_FALSE(safe_strtol("-2147483649", val)); // (- (expt -2.0 31) 1)
123     */
124 
125     /* We'll allow space to terminate the string.  And leading space. */
126     EXPECT_TRUE(safe_strtol(" 123 foo", val));
127     EXPECT_EQ(123, val);
128 }
129 
TEST(StringTest, safe_strtof)130 TEST(StringTest, safe_strtof) {
131     float val;
132     EXPECT_TRUE(safe_strtof("123", val));
133     EXPECT_EQ(123.00f, val);
134     EXPECT_TRUE(safe_strtof("+123", val));
135     EXPECT_EQ(123.00f, val);
136     EXPECT_TRUE(safe_strtof("-123", val));
137     EXPECT_EQ(-123.00f, val);
138     EXPECT_FALSE(safe_strtof("", val));  /* empty */
139     EXPECT_FALSE(safe_strtof("123BOGUS", val));  /* non-numeric */
140 
141     /* We'll allow space to terminate the string.  And leading space. */
142     EXPECT_TRUE(safe_strtof(" 123 foo", val));
143     EXPECT_EQ(123.00f, val);
144 
145     EXPECT_TRUE(safe_strtof("123.23", val));
146     EXPECT_EQ(123.23f, val);
147 
148     EXPECT_TRUE(safe_strtof("123.00", val));
149     EXPECT_EQ(123.00f, val);
150 }
151 
TEST(StringTest, split_string)152 TEST(StringTest, split_string) {
153     using namespace testing;
154 
155     EXPECT_THAT(split_string("123:456", ":"), ElementsAre("123", "456"));
156     EXPECT_THAT(split_string("123::456", ":"), ElementsAre("123", "", "456"));
157     EXPECT_THAT(split_string("123:456:", ":"), ElementsAre("123", "456", ""));
158     EXPECT_THAT(split_string("123:456:789", ":", 1),
159                 ElementsAre("123", "456:789"));
160     EXPECT_THAT(split_string("123:456:789", ":", 2),
161                 ElementsAre("123", "456", "789"));
162     EXPECT_THAT(split_string("123::456", ":", 1),
163                 ElementsAre("123", ":456"));
164     EXPECT_THAT(split_string(":", ":", 2),
165                 ElementsAre("", ""));
166     EXPECT_THAT(split_string(":abcd", ":", 200),
167                 ElementsAre("", "abcd"));
168     EXPECT_THAT(split_string("Hello, World!", ", ", 200),
169                 ElementsAre("Hello", "World!"));
170     EXPECT_THAT(split_string("Hello<BOOM>World<BOOM>!", "<BOOM>", 200),
171                 ElementsAre("Hello", "World", "!"));
172     EXPECT_THAT(split_string("Hello<BOOM>World<BOOM>!", "<BOOM>", 1),
173                 ElementsAre("Hello", "World<BOOM>!"));
174 }
175 
TEST(StringTest, percent_decode)176 TEST(StringTest, percent_decode) {
177     // Test every character from 0x00->0xFF that they can be converted to
178     // percent encoded strings and back again
179     for (int i = 0; i < 255; ++i) {
180         std::stringstream s, t;
181         s << "%" << std::setfill('0') << std::setw(2) << std::hex << i;
182         t << static_cast<char>(i);
183         EXPECT_EQ(t.str(), percent_decode(s.str()));
184     }
185 
186     EXPECT_EQ("abcdef!abcdef", percent_decode("abcdef%21abcdef"));
187     EXPECT_EQ("!", percent_decode("%21"));
188     EXPECT_EQ("!!", percent_decode("%21%21"));
189     EXPECT_EQ("%21", percent_decode("%25%32%31"));
190 
191     EXPECT_THROW(percent_decode("%"), std::invalid_argument);
192     EXPECT_THROW(percent_decode("%%"), std::invalid_argument);
193     EXPECT_THROW(percent_decode("%3"), std::invalid_argument);
194     EXPECT_THROW(percent_decode("%%%"), std::invalid_argument);
195     EXPECT_THROW(percent_decode("%GG"), std::invalid_argument);
196 }
197 
TEST(StringTest, decode_query)198 TEST(StringTest, decode_query) {
199     using namespace testing;
200     std::pair<std::string, StrToStrMap> request;
201 
202     request = decode_query("key?arg=val&arg2=val2&arg3=val?=");
203     EXPECT_EQ("key", request.first);
204     EXPECT_THAT(request.second, UnorderedElementsAre(Pair("arg", "val"),
205                                                      Pair("arg2", "val2"),
206                                                      Pair("arg3", "val?=")));
207 
208     request = decode_query("key");
209     EXPECT_EQ("key", request.first);
210     EXPECT_THAT(request.second, UnorderedElementsAre());
211 
212     request = decode_query("key?");
213     EXPECT_EQ("key", request.first);
214     EXPECT_THAT(request.second, UnorderedElementsAre());
215 
216     request = decode_query("key\?\?=?");
217     EXPECT_EQ("key", request.first);
218     EXPECT_THAT(request.second, UnorderedElementsAre(Pair("?", "?")));
219 
220     request = decode_query("key?%25=%26&%26=%25");
221     EXPECT_EQ("key", request.first);
222     EXPECT_THAT(request.second, UnorderedElementsAre(Pair("%", "&"),
223                                                      Pair("&", "%")));
224 
225     EXPECT_THROW(decode_query("key?=&a=b"), std::invalid_argument);
226     EXPECT_THROW(decode_query("key?a&a=b"), std::invalid_argument);
227 
228 }
229 
trim(char* ptr)230 static char* trim(char* ptr) {
231     char *start = ptr;
232     char *end;
233 
234     while (isspace(*start)) {
235         ++start;
236     }
237     end = start + strlen(start) - 1;
238     if (end != start) {
239         while (isspace(*end)) {
240             *end = '\0';
241             --end;
242         }
243     }
244     return start;
245 }
246 
TEST(ConfigParserTest, A)247 TEST(ConfigParserTest, A) {
248     bool bool_val = false;
249     size_t size_val = 0;
250     ssize_t ssize_val = 0;
251     float float_val = 0;
252     char *string_val = 0;
253     int ii;
254     char buffer[1024];
255     FILE *cfg;
256     char outfile[sizeof(TMP_TEMPLATE)+1];
257     char cfgfile[sizeof(TMP_TEMPLATE)+1];
258     FILE *error;
259 
260     /* Set up the different items I can handle */
261     struct config_item items[7];
262     memset(&items, 0, sizeof(items));
263     ii = 0;
264     items[ii].key = "bool";
265     items[ii].datatype = DT_BOOL;
266     items[ii].value.dt_bool = &bool_val;
267     ++ii;
268 
269     items[ii].key = "size_t";
270     items[ii].datatype = DT_SIZE;
271     items[ii].value.dt_size = &size_val;
272     ++ii;
273 
274     items[ii].key = "ssize_t";
275     items[ii].datatype = DT_SSIZE;
276     items[ii].value.dt_ssize = &ssize_val;
277     ++ii;
278 
279     items[ii].key = "float";
280     items[ii].datatype = DT_FLOAT;
281     items[ii].value.dt_float = &float_val;
282     ++ii;
283 
284     items[ii].key = "string";
285     items[ii].datatype = DT_STRING;
286     items[ii].value.dt_string = &string_val;
287     ++ii;
288 
289     items[ii].key = "config_file";
290     items[ii].datatype = DT_CONFIGFILE;
291     ++ii;
292 
293     items[ii].key = NULL;
294     ++ii;
295 
296     ASSERT_EQ(7, ii);
297     strncpy(outfile, TMP_TEMPLATE, sizeof(TMP_TEMPLATE)+1);
298     strncpy(cfgfile, TMP_TEMPLATE, sizeof(TMP_TEMPLATE)+1);
299 
300     ASSERT_NE(cb_mktemp(outfile), nullptr);
301     error = fopen(outfile, "w");
302 
303     ASSERT_NE(error, nullptr);
304     ASSERT_EQ(0, parse_config("", items, error));
305     /* Nothing should be found */
306     for (ii = 0; ii < 5; ++ii) {
307         EXPECT_FALSE(items[0].found);
308     }
309 
310     ASSERT_EQ(0, parse_config("bool=true", items, error));
311     EXPECT_TRUE(bool_val);
312     /* only bool should be found */
313     EXPECT_TRUE(items[0].found);
314     items[0].found = false;
315     for (ii = 0; ii < 5; ++ii) {
316         EXPECT_FALSE(items[0].found);
317     }
318 
319     /* It should allow illegal keywords */
320     ASSERT_EQ(1, parse_config("pacman=dead", items, error));
321     /* and illegal values */
322     ASSERT_EQ(-1, parse_config("bool=12", items, error));
323     EXPECT_FALSE(items[0].found);
324     /* and multiple occurences of the same value */
325     ASSERT_EQ(0, parse_config("size_t=1; size_t=1024", items, error));
326     EXPECT_TRUE(items[1].found);
327     EXPECT_EQ(1024u, size_val);
328     items[1].found = false;
329 
330     /* Empty string */
331     /* XXX:  This test fails on Linux, but works on OS X.
332     cb_assert(parse_config("string=", items, error) == 0);
333     cb_assert(items[4].found);
334     cb_assert(strcmp(string_val, "") == 0);
335     items[4].found = false;
336     */
337     /* Plain string */
338     ASSERT_EQ(0, parse_config("string=sval", items, error));
339     EXPECT_TRUE(items[4].found);
340     EXPECT_STREQ("sval", string_val);
341     items[4].found = false;
342     cb_free(string_val);
343     /* Leading space */
344     ASSERT_EQ(0, parse_config("string= sval", items, error));
345     EXPECT_TRUE(items[4].found);
346     EXPECT_STREQ("sval", string_val);
347     items[4].found = false;
348     cb_free(string_val);
349     /* Escaped leading space */
350     ASSERT_EQ(0, parse_config("string=\\ sval", items, error));
351     EXPECT_TRUE(items[4].found);
352     EXPECT_STREQ(" sval", string_val);
353     items[4].found = false;
354     cb_free(string_val);
355     /* trailing space */
356     ASSERT_EQ(0, parse_config("string=sval ", items, error));
357     EXPECT_TRUE(items[4].found);
358     EXPECT_STREQ("sval", string_val);
359     items[4].found = false;
360     cb_free(string_val);
361     /* escaped trailing space */
362     ASSERT_EQ(0, parse_config("string=sval\\ ", items, error));
363     EXPECT_TRUE(items[4].found);
364     EXPECT_STREQ("sval ", string_val);
365     items[4].found = false;
366     cb_free(string_val);
367     /* escaped stop char */
368     ASSERT_EQ(0, parse_config("string=sval\\;blah=x", items, error));
369     EXPECT_TRUE(items[4].found);
370     EXPECT_STREQ("sval;blah=x", string_val);
371     items[4].found = false;
372     cb_free(string_val);
373     /* middle space */
374     ASSERT_EQ(0, parse_config("string=s val", items, error));
375     EXPECT_TRUE(items[4].found);
376     EXPECT_STREQ("s val", string_val);
377     items[4].found = false;
378     cb_free(string_val);
379 
380     /* And all of the variables */
381     ASSERT_EQ(0, parse_config("bool=true;size_t=1024;float=12.5;string=somestr",
382                               items, error));
383     EXPECT_TRUE(bool_val);
384     EXPECT_EQ(1024u, size_val);
385     EXPECT_EQ(12.5f, float_val);
386     EXPECT_STREQ("somestr", string_val);
387     cb_free(string_val);
388     for (ii = 0; ii < 5; ++ii) {
389         items[ii].found = false;
390     }
391 
392     ASSERT_EQ(0, parse_config("size_t=1k", items, error));
393     EXPECT_TRUE(items[1].found);
394     EXPECT_EQ(1024u, size_val);
395     items[1].found = false;
396     ASSERT_EQ(0, parse_config("size_t=1m", items, error));
397     EXPECT_TRUE(items[1].found);
398     EXPECT_EQ(1024u * 1024u, size_val);
399     items[1].found = false;
400     ASSERT_EQ(0, parse_config("size_t=1g", items, error));
401     EXPECT_TRUE(items[1].found);
402     EXPECT_EQ(1024u * 1024u * 1024u ,size_val);
403     items[1].found = false;
404     ASSERT_EQ(0, parse_config("size_t=1K", items, error));
405     EXPECT_TRUE(items[1].found);
406     EXPECT_EQ(1024u, size_val);
407     items[1].found = false;
408     ASSERT_EQ(0, parse_config("size_t=1M", items, error));
409     EXPECT_TRUE(items[1].found);
410     EXPECT_EQ(1024u * 1024u, size_val);
411     items[1].found = false;
412     ASSERT_EQ(0, parse_config("size_t=1G", items, error));
413     EXPECT_TRUE(items[1].found);
414     EXPECT_EQ(1024u * 1024u * 1024u, size_val);
415     items[1].found = false;
416 
417     // Check negative and positive input
418     std::vector<std::pair<std::string, int> >suffixes = {{ "k", 1024},
419                                                          {"m", 1024*1024},
420                                                          {"g", 1024*1024*1024},
421                                                          {"K", 1024},
422                                                          {"M", 1024*1024},
423                                                          {"G", 1024*1024*1024}};
424 
425     /*
426      * This is a hack to work around problems with Visual Studio in
427      * debug builds. Initially the construct looked like:
428      *
429      *    for (ssize_t test_val : { -1000, -1, 0, 1, 1000 );
430      *
431      * but that results in
432      *
433      *    SEH exception with code 0xc0000005 thrown in the test body
434      */
435     const ssize_t values[5] = { -1000, -1, 0, 1, 1000 };
436     for (int ii = 0; ii < 5; ++ii) {
437         const ssize_t test_val = values[ii];
438         for (auto suffix : suffixes) {
439             std::string config = "ssize_t=" +
440                                  std::to_string(test_val) + suffix.first;
441             ASSERT_EQ(0, parse_config(config.c_str(), items, error));
442             EXPECT_TRUE(items[2].found);
443             EXPECT_EQ(suffix.second * test_val, ssize_val);
444             items[2].found = false;
445         }
446     }
447 
448     ASSERT_NE(cb_mktemp(cfgfile), nullptr);
449     cfg = fopen(cfgfile, "w");
450     ASSERT_NE(cfg, nullptr);
451     fprintf(cfg, "# This is a config file\nbool=true\nsize_t=1023\nfloat=12.4\n");
452     fclose(cfg);
453     sprintf(buffer, "config_file=%s", cfgfile);
454     ASSERT_EQ(0, parse_config(buffer, items, error));
455     EXPECT_TRUE(bool_val);
456     EXPECT_EQ(1023u, size_val);
457     EXPECT_EQ(12.4f, float_val);
458     fclose(error);
459 
460     remove(cfgfile);
461     /* Verify that I received the error messages ;-) */
462     error = fopen(outfile, "r");
463     ASSERT_TRUE(error);
464 
465     EXPECT_TRUE(fgets(buffer, sizeof(buffer), error));
466     EXPECT_STREQ("Unsupported key: <pacman>", trim(buffer));
467     EXPECT_TRUE(fgets(buffer, sizeof(buffer), error));
468     EXPECT_STREQ("Invalid entry, Key: <bool> Value: <12>", trim(buffer));
469     EXPECT_TRUE(fgets(buffer, sizeof(buffer), error));
470     EXPECT_STREQ("WARNING: Found duplicate entry for \"size_t\"", trim(buffer));
471     EXPECT_EQ(nullptr, fgets(buffer, sizeof(buffer), error));
472 
473     EXPECT_EQ(0, fclose(error));
474     remove(outfile);
475 }
476