1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <fuzz-utils/string-list.h>
6 #include <unittest/unittest.h>
7 
8 namespace fuzzing {
9 namespace testing {
10 namespace {
11 
12 #define arraysize(x) sizeof(x) / sizeof(x[0])
13 
14 // Helper function to find expected strings in a list
Match(StringList * list,const char ** expected,size_t off,size_t len)15 bool Match(StringList* list, const char** expected, size_t off, size_t len) {
16     BEGIN_HELPER;
17     EXPECT_EQ(list->length(), len);
18     const char* elem = list->first();
19     for (size_t i = 0; i < len; ++i) {
20         ASSERT_NONNULL(elem);
21         EXPECT_STR_EQ(elem, expected[off + i]);
22         elem = list->next();
23     }
24     EXPECT_NULL(elem);
25     END_HELPER;
26 }
27 
TestEmpty()28 bool TestEmpty() {
29     BEGIN_TEST;
30     StringList list;
31 
32     EXPECT_TRUE(list.is_empty());
33     EXPECT_NULL(list.first());
34     EXPECT_NULL(list.next());
35 
36     END_TEST;
37 }
38 
TestPushFrontAndBack()39 bool TestPushFrontAndBack() {
40     BEGIN_TEST;
41     StringList list;
42     const char* expected[] = {"", "foo", "bar", "baz", ""};
43 
44     // Strings can be pushed from either end
45     list.push_front("bar");
46     list.push_back("baz");
47     list.push_front("foo");
48     EXPECT_TRUE(Match(&list, expected, 1, 3));
49 
50     // Empty strings are fine
51     list.push_front("");
52     list.push_back("");
53     EXPECT_TRUE(Match(&list, expected, 0, 5));
54 
55     // Null strings are ignored
56     list.push_front(nullptr);
57     list.push_back(nullptr);
58     EXPECT_TRUE(Match(&list, expected, 0, 5));
59 
60     // Test the new constructor
61     StringList list2(expected, arraysize(expected));
62     EXPECT_TRUE(Match(&list, expected, 0, 5));
63 
64     END_TEST;
65 }
66 
TestKeepIf()67 bool TestKeepIf() {
68     BEGIN_TEST;
69     StringList list;
70     const char* original[] = {"",     "foo",   "bar",    "baz",    "qux",
71                               "quux", "corge", "grault", "garply", "waldo",
72                               "fred", "plugh", "xyzzy",  "thud",   ""};
73 
74     const char* expected1[] = {"bar", "corge", "grault", "garply", "plugh"};
75 
76     const char* expected2[] = {"corge", "grault", "garply", "plugh"};
77 
78     const char* expected3[] = {"garply"};
79 
80     for (size_t i = 0; i < arraysize(original); ++i) {
81         list.push_back(original[i]);
82     }
83 
84     // Null string has no effect
85     list.keep_if(nullptr);
86     EXPECT_TRUE(Match(&list, original, 0, arraysize(original)));
87 
88     // Empty string matches everything
89     list.keep_if("");
90     EXPECT_TRUE(Match(&list, original, 0, arraysize(original)));
91 
92     // Match a string
93     list.keep_if("g");
94     EXPECT_TRUE(Match(&list, expected2, 0, arraysize(expected2)));
95 
96     // Match a string that would have matched elements in the original list
97     list.keep_if("ar");
98     EXPECT_TRUE(Match(&list, expected3, 0, arraysize(expected3)));
99 
100     // Use a string that doesn't match anything
101     list.keep_if("zzz");
102     EXPECT_TRUE(list.is_empty());
103 
104     // Reset and apply both matches at once with logical-or
105     StringList substrs;
106     substrs.push_back("g");
107     substrs.push_back("ar");
108 
109     list.clear();
110     for (size_t i = 0; i < arraysize(original); ++i) {
111         list.push_back(original[i]);
112     }
113     list.keep_if_any(&substrs);
114     EXPECT_TRUE(Match(&list, expected1, 0, arraysize(expected1)));
115 
116     // Reset and apply both matches at once with logical-and
117     list.clear();
118     for (size_t i = 0; i < arraysize(original); ++i) {
119         list.push_back(original[i]);
120     }
121     list.keep_if_all(&substrs);
122     EXPECT_TRUE(Match(&list, expected3, 0, arraysize(expected3)));
123 
124     END_TEST;
125 }
126 
TestEraseIf()127 bool TestEraseIf() {
128     BEGIN_TEST;
129     StringList list;
130     const char* original[] = {"", "foo", "bar", "baz", ""};
131 
132     const char* expected1[] = {"", "foo", "baz", ""};
133 
134     const char* expected2[] = {"foo", "baz"};
135 
136     for (size_t i = 0; i < sizeof(original) / sizeof(original[0]); ++i) {
137         list.push_back(original[i]);
138     }
139 
140     // Null and empty strings have no effect
141     list.erase_if(nullptr);
142     EXPECT_TRUE(Match(&list, original, 0, arraysize(original)));
143 
144     // Use a string that doesn't match anything
145     list.erase_if("zzz");
146     EXPECT_TRUE(Match(&list, original, 0, arraysize(original)));
147 
148     // Match a string
149     list.erase_if("bar");
150     EXPECT_TRUE(Match(&list, expected1, 0, arraysize(expected1)));
151 
152     // Idempotent
153     list.erase_if("bar");
154     EXPECT_TRUE(Match(&list, expected1, 0, arraysize(expected1)));
155 
156     // Able to erase empty strings
157     list.erase_if("");
158     EXPECT_TRUE(Match(&list, expected2, 0, arraysize(expected2)));
159 
160     END_TEST;
161 }
162 
TestClear()163 bool TestClear() {
164     BEGIN_TEST;
165 
166     StringList list;
167     list.push_front("bar");
168 
169     EXPECT_NONNULL(list.first());
170     list.clear();
171     EXPECT_NULL(list.next());
172     EXPECT_NULL(list.first());
173     EXPECT_EQ(list.length(), 0);
174 
175     END_TEST;
176 }
177 
178 BEGIN_TEST_CASE(StringListTest)
179 RUN_TEST(TestEmpty)
180 RUN_TEST(TestPushFrontAndBack)
181 RUN_TEST(TestKeepIf)
182 RUN_TEST(TestEraseIf)
183 RUN_TEST(TestClear)
184 END_TEST_CASE(StringListTest)
185 
186 } // namespace
187 } // namespace testing
188 } // namespace fuzzing
189