1 | // Copyright (c) 2005, Google Inc.
|
---|
2 | // All rights reserved.
|
---|
3 | //
|
---|
4 | // Redistribution and use in source and binary forms, with or without
|
---|
5 | // modification, are permitted provided that the following conditions are
|
---|
6 | // met:
|
---|
7 | //
|
---|
8 | // * Redistributions of source code must retain the above copyright
|
---|
9 | // notice, this list of conditions and the following disclaimer.
|
---|
10 | // * Redistributions in binary form must reproduce the above
|
---|
11 | // copyright notice, this list of conditions and the following disclaimer
|
---|
12 | // in the documentation and/or other materials provided with the
|
---|
13 | // distribution.
|
---|
14 | // * Neither the name of Google Inc. nor the names of its
|
---|
15 | // contributors may be used to endorse or promote products derived from
|
---|
16 | // this software without specific prior written permission.
|
---|
17 | //
|
---|
18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
---|
19 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
---|
20 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
---|
21 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
---|
22 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
---|
23 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
---|
24 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
---|
25 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
---|
26 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
---|
27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
---|
28 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
---|
29 |
|
---|
30 | // ---
|
---|
31 | // Author: Craig Silverstein
|
---|
32 | //
|
---|
33 | // This tests <google/sparsetable>
|
---|
34 | //
|
---|
35 | // Since sparsetable is templatized, it's important that we test every
|
---|
36 | // function in every class in this file -- not just to see if it
|
---|
37 | // works, but even if it compiles.
|
---|
38 |
|
---|
39 | #include <google/sparsehash/config.h>
|
---|
40 | #include <string>
|
---|
41 | #include <stdio.h>
|
---|
42 | #include <string.h> // for memcmp()
|
---|
43 | #include <stdlib.h> // defines unlink() on some windows platforms(?)
|
---|
44 | #ifdef HAVE_UNISTD_H
|
---|
45 | #include <unistd.h> // for unlink()
|
---|
46 | #endif
|
---|
47 | #include <google/sparsetable>
|
---|
48 |
|
---|
49 | using STL_NAMESPACE::string;
|
---|
50 |
|
---|
51 | // Many sparsetable operations return a size_t. Unfortunately, there's
|
---|
52 | // no portable way to pass a size_t to snprintf(). This macro casts these
|
---|
53 | // things to an unsigned long, which should be big enough for a size_t.
|
---|
54 | #define UL(x) ( static_cast<unsigned long>(x) )
|
---|
55 |
|
---|
56 |
|
---|
57 | static char outbuf[10240]; // big enough for these tests
|
---|
58 | static char* out = outbuf; // where to write next
|
---|
59 | #define LEFT (outbuf + sizeof(outbuf) - out)
|
---|
60 |
|
---|
61 | #define TEST(cond) out += snprintf(out, LEFT, #cond "? %s\n", \
|
---|
62 | cond ? "yes" : "no");
|
---|
63 |
|
---|
64 | using GOOGLE_NAMESPACE::sparsetable;
|
---|
65 |
|
---|
66 | // replaces buf, in-place, with a version of buf that lacks \r's.
|
---|
67 | static int strip_cr(char* buf, int len) {
|
---|
68 | char *r, *w;
|
---|
69 | for ( r = buf, w = buf; r - buf < len; r++ ) {
|
---|
70 | if (*r != '\r')
|
---|
71 | *w++ = *r;
|
---|
72 | }
|
---|
73 | return w - buf;
|
---|
74 | }
|
---|
75 |
|
---|
76 | int main(int argc, char **argv) { // though we ignore the args
|
---|
77 | sparsetable<int> x(7), y(70), z;
|
---|
78 | x.set(4, 10);
|
---|
79 | y.set(12, -12);
|
---|
80 | y.set(47, -47);
|
---|
81 | y.set(48, -48);
|
---|
82 | y.set(49, -49);
|
---|
83 |
|
---|
84 | const sparsetable<int> constx(x);
|
---|
85 | const sparsetable<int> consty(y);
|
---|
86 |
|
---|
87 | // ----------------------------------------------------------------------
|
---|
88 | // Test the plain iterators
|
---|
89 |
|
---|
90 | for ( sparsetable<int>::iterator it = x.begin(); it != x.end(); ++it ) {
|
---|
91 | out += snprintf(out, LEFT, "x[%lu]: %d\n", UL(it - x.begin()), int(*it));
|
---|
92 | }
|
---|
93 | for ( sparsetable<int>::const_iterator it = x.begin(); it != x.end(); ++it ) {
|
---|
94 | out += snprintf(out, LEFT, "x[%lu]: %d\n", UL(it - x.begin()), *it);
|
---|
95 | }
|
---|
96 | for ( sparsetable<int>::reverse_iterator it = x.rbegin(); it != x.rend(); ++it ) {
|
---|
97 | out += snprintf(out, LEFT, "x[%lu]: %d\n", UL(x.rend()-1 - it), int(*it));
|
---|
98 | }
|
---|
99 | for ( sparsetable<int>::const_reverse_iterator it = constx.rbegin(); it != constx.rend(); ++it ) {
|
---|
100 | out += snprintf(out, LEFT, "x[%lu]: %d\n", UL(constx.rend()-1 - it), *it);
|
---|
101 | }
|
---|
102 | for ( sparsetable<int>::iterator it = z.begin(); it != z.end(); ++it ) {
|
---|
103 | out += snprintf(out, LEFT, "z[%lu]: %d\n", UL(it - z.begin()), int(*it));
|
---|
104 | }
|
---|
105 |
|
---|
106 | { // array version
|
---|
107 | out += snprintf(out, LEFT, "x[3]: %d\n", int(x[3]));
|
---|
108 | out += snprintf(out, LEFT, "x[4]: %d\n", int(x[4]));
|
---|
109 | out += snprintf(out, LEFT, "x[5]: %d\n", int(x[5]));
|
---|
110 | }
|
---|
111 | {
|
---|
112 | sparsetable<int>::iterator it; // non-const version
|
---|
113 | out += snprintf(out, LEFT, "x[4]: %d\n", int(x.begin()[4]));
|
---|
114 | it = x.begin() + 4; // should point to the non-zero value
|
---|
115 | out += snprintf(out, LEFT, "x[4]: %d\n", int(*it));
|
---|
116 | it--;
|
---|
117 | --it;
|
---|
118 | it += 5;
|
---|
119 | it -= 2;
|
---|
120 | it++;
|
---|
121 | ++it;
|
---|
122 | it = it - 3;
|
---|
123 | it = 1 + it; // now at 5
|
---|
124 | out += snprintf(out, LEFT, "x[3]: %d\n", int(it[-2]));
|
---|
125 | out += snprintf(out, LEFT, "x[4]: %d\n", int(it[-1]));
|
---|
126 | *it = 55;
|
---|
127 | out += snprintf(out, LEFT, "x[5]: %d\n", int(it[0]));
|
---|
128 | out += snprintf(out, LEFT, "x[5]: %d\n", int(*it));
|
---|
129 | int *x6 = &(it[1]);
|
---|
130 | *x6 = 66;
|
---|
131 | out += snprintf(out, LEFT, "x[6]: %d\n", int(*(it + 1)));
|
---|
132 | }
|
---|
133 | {
|
---|
134 | sparsetable<int>::const_iterator it; // const version
|
---|
135 | out += snprintf(out, LEFT, "x[4]: %d\n", int(x.begin()[4]));
|
---|
136 | it = x.begin() + 4; // should point to the non-zero value
|
---|
137 | out += snprintf(out, LEFT, "x[4]: %d\n", *it);
|
---|
138 | it--;
|
---|
139 | --it;
|
---|
140 | it += 5;
|
---|
141 | it -= 2;
|
---|
142 | it++;
|
---|
143 | ++it;
|
---|
144 | it = it - 3;
|
---|
145 | it = 1 + it; // now at 5
|
---|
146 | out += snprintf(out, LEFT, "x[3]: %d\n", it[-2]);
|
---|
147 | out += snprintf(out, LEFT, "x[4]: %d\n", it[-1]);
|
---|
148 | out += snprintf(out, LEFT, "x[5]: %d\n", *it);
|
---|
149 | out += snprintf(out, LEFT, "x[6]: %d\n", *(it + 1));
|
---|
150 | }
|
---|
151 |
|
---|
152 | TEST(x.begin() == x.begin() + 1 - 1);
|
---|
153 | TEST(x.begin() < x.end());
|
---|
154 | TEST(z.begin() < z.end());
|
---|
155 | TEST(z.begin() <= z.end());
|
---|
156 | TEST(z.begin() == z.end());
|
---|
157 |
|
---|
158 |
|
---|
159 | // ----------------------------------------------------------------------
|
---|
160 | // Test the non-empty iterators
|
---|
161 |
|
---|
162 | for ( sparsetable<int>::nonempty_iterator it = x.nonempty_begin(); it != x.nonempty_end(); ++it ) {
|
---|
163 | out += snprintf(out, LEFT, "x[??]: %d\n", *it);
|
---|
164 | }
|
---|
165 | for ( sparsetable<int>::const_nonempty_iterator it = y.nonempty_begin(); it != y.nonempty_end(); ++it ) {
|
---|
166 | out += snprintf(out, LEFT, "y[??]: %d\n", *it);
|
---|
167 | }
|
---|
168 | for ( sparsetable<int>::reverse_nonempty_iterator it = y.nonempty_rbegin(); it != y.nonempty_rend(); ++it ) {
|
---|
169 | out += snprintf(out, LEFT, "y[??]: %d\n", *it);
|
---|
170 | }
|
---|
171 | for ( sparsetable<int>::const_reverse_nonempty_iterator it = consty.nonempty_rbegin(); it != consty.nonempty_rend(); ++it ) {
|
---|
172 | out += snprintf(out, LEFT, "y[??]: %d\n", *it);
|
---|
173 | }
|
---|
174 | for ( sparsetable<int>::nonempty_iterator it = z.nonempty_begin(); it != z.nonempty_end(); ++it ) {
|
---|
175 | out += snprintf(out, LEFT, "z[??]: %d\n", *it);
|
---|
176 | }
|
---|
177 |
|
---|
178 | {
|
---|
179 | sparsetable<int>::nonempty_iterator it; // non-const version
|
---|
180 | out += snprintf(out, LEFT, "first non-empty y: %d\n", *y.nonempty_begin());
|
---|
181 | out += snprintf(out, LEFT, "first non-empty x: %d\n", *x.nonempty_begin());
|
---|
182 | it = x.nonempty_begin();
|
---|
183 | ++it; // should be at end
|
---|
184 | --it;
|
---|
185 | out += snprintf(out, LEFT, "first non-empty x: %d\n", *it++);
|
---|
186 | it--;
|
---|
187 | out += snprintf(out, LEFT, "first non-empty x: %d\n", *it++);
|
---|
188 | }
|
---|
189 | {
|
---|
190 | sparsetable<int>::const_nonempty_iterator it; // non-const version
|
---|
191 | out += snprintf(out, LEFT, "first non-empty y: %d\n", *y.nonempty_begin());
|
---|
192 | out += snprintf(out, LEFT, "first non-empty x: %d\n", *x.nonempty_begin());
|
---|
193 | it = x.nonempty_begin();
|
---|
194 | ++it; // should be at end
|
---|
195 | --it;
|
---|
196 | out += snprintf(out, LEFT, "first non-empty x: %d\n", *it++);
|
---|
197 | it--;
|
---|
198 | out += snprintf(out, LEFT, "first non-empty x: %d\n", *it++);
|
---|
199 | }
|
---|
200 |
|
---|
201 | TEST(x.begin() == x.begin() + 1 - 1);
|
---|
202 | TEST(z.begin() != z.end());
|
---|
203 |
|
---|
204 | // ----------------------------------------------------------------------
|
---|
205 | // Test sparsetable functions
|
---|
206 | out += snprintf(out, LEFT, "x has %lu/%lu buckets, "
|
---|
207 | "y %lu/%lu, z %lu/%lu\n",
|
---|
208 | UL(x.num_nonempty()), UL(x.size()),
|
---|
209 | UL(y.num_nonempty()), UL(y.size()),
|
---|
210 | UL(z.num_nonempty()), UL(z.size()));
|
---|
211 |
|
---|
212 | y.resize(48); // should get rid of 48 and 49
|
---|
213 | y.resize(70); // 48 and 49 should still be gone
|
---|
214 | out += snprintf(out, LEFT, "y shrank and grew: it's now %lu/%lu\n",
|
---|
215 | UL(y.num_nonempty()), UL(y.size()));
|
---|
216 | out += snprintf(out, LEFT, "y[12] = %d, y.get(12) = %d\n", int(y[12]), y.get(12));
|
---|
217 | y.erase(12);
|
---|
218 | out += snprintf(out, LEFT, "y[12] cleared. y now %lu/%lu. "
|
---|
219 | "y[12] = %d, y.get(12) = %d\n",
|
---|
220 | UL(y.num_nonempty()), UL(y.size()), int(y[12]), y.get(12));
|
---|
221 |
|
---|
222 | swap(x, y);
|
---|
223 |
|
---|
224 | y.clear();
|
---|
225 | TEST(y == z);
|
---|
226 |
|
---|
227 | y.resize(70);
|
---|
228 | for ( int i = 10; i < 40; ++i )
|
---|
229 | y[i] = -i;
|
---|
230 | y.erase(y.begin() + 15, y.begin() + 30);
|
---|
231 | y.erase(y.begin() + 34);
|
---|
232 | y.erase(12);
|
---|
233 | y.resize(38);
|
---|
234 | y.resize(10000);
|
---|
235 | y[9898] = -9898;
|
---|
236 | for ( sparsetable<int>::const_iterator it = y.begin(); it != y.end(); ++it ) {
|
---|
237 | if ( y.test(it) )
|
---|
238 | out += snprintf(out, LEFT, "y[%lu] is set\n", UL(it - y.begin()));
|
---|
239 | }
|
---|
240 | out += snprintf(out, LEFT, "That's %lu set buckets\n", UL(y.num_nonempty()));
|
---|
241 |
|
---|
242 | out += snprintf(out, LEFT, "Starting from y[32]...\n");
|
---|
243 | for ( sparsetable<int>::const_nonempty_iterator it = y.get_iter(32);
|
---|
244 | it != y.nonempty_end(); ++it )
|
---|
245 | out += snprintf(out, LEFT, "y[??] = %d\n", *it);
|
---|
246 |
|
---|
247 | out += snprintf(out, LEFT, "From y[32] down...\n");
|
---|
248 | for ( sparsetable<int>::nonempty_iterator it = y.get_iter(32);
|
---|
249 | it != y.nonempty_begin(); )
|
---|
250 | out += snprintf(out, LEFT, "y[??] = %d\n", *--it);
|
---|
251 |
|
---|
252 | // ----------------------------------------------------------------------
|
---|
253 | // Test I/O
|
---|
254 | const char *file = "/tmp/#sparsetable.test";
|
---|
255 | FILE *fp = fopen(file, "wb");
|
---|
256 | if ( fp == NULL ) {
|
---|
257 | // maybe we can't write to /tmp/. Try the current directory
|
---|
258 | file = "#sparsetable.test";
|
---|
259 | fp = fopen(file, "wb");
|
---|
260 | }
|
---|
261 | if ( fp == NULL ) {
|
---|
262 | out += snprintf(out, LEFT, "Can't open %s, skipping disk write...\n", file);
|
---|
263 | } else {
|
---|
264 | y.write_metadata(fp); // only write meta-information
|
---|
265 | y.write_nopointer_data(fp);
|
---|
266 | fclose(fp);
|
---|
267 | }
|
---|
268 | fp = fopen(file, "rb");
|
---|
269 | if ( fp == NULL ) {
|
---|
270 | out += snprintf(out, LEFT, "Can't open %s, skipping disk read...\n", file);
|
---|
271 | } else {
|
---|
272 | sparsetable<int> y2;
|
---|
273 | y2.read_metadata(fp);
|
---|
274 | y2.read_nopointer_data(fp);
|
---|
275 | fclose(fp);
|
---|
276 |
|
---|
277 | for ( sparsetable<int>::const_iterator it = y2.begin(); it != y2.end(); ++it ) {
|
---|
278 | if ( y2.test(it) )
|
---|
279 | out += snprintf(out, LEFT, "y2[%lu] is %d\n", UL(it - y2.begin()), *it);
|
---|
280 | }
|
---|
281 | out += snprintf(out, LEFT, "That's %lu set buckets\n", UL(y2.num_nonempty()));
|
---|
282 | }
|
---|
283 | unlink(file);
|
---|
284 |
|
---|
285 | // Finally, check to see if our output (in out) is what it's supposed to be.
|
---|
286 | char expected[sizeof(outbuf) + 10];
|
---|
287 | string filename = (string(getenv("srcdir") ? getenv("srcdir") : ".") +
|
---|
288 | "/src/sparsetable_unittest.expected");
|
---|
289 | FILE* expected_fp = fopen(filename.c_str(), "rb");
|
---|
290 | if ( expected_fp == NULL ) {
|
---|
291 | fprintf(stderr, "Can't judge test: can't load 'golden' file '%s'.\n",
|
---|
292 | filename.c_str());
|
---|
293 | return 2;
|
---|
294 | }
|
---|
295 | int r = fread(expected, 1, sizeof(expected) - 1, expected_fp);
|
---|
296 | r = strip_cr(expected, r);
|
---|
297 | if ( r != out - outbuf || // output not the same size
|
---|
298 | memcmp(outbuf, expected, r) ) { // or bytes differed
|
---|
299 | fprintf(stderr, "TESTS FAILED\n\nEXPECTED:\n\n%s\n\nACTUAL:\n\n%s\n\n",
|
---|
300 | expected, outbuf);
|
---|
301 | return 1;
|
---|
302 | } else {
|
---|
303 | printf("All tests passed.\n");
|
---|
304 | return 0;
|
---|
305 | }
|
---|
306 | }
|
---|