1 : /*
2 : * Copyright (c) 2012 The Native Client Authors. All rights reserved.
3 : * Use of this source code is governed by a BSD-style license that can be
4 : * found in the LICENSE file.
5 : */
6 :
7 : #include <stdio.h>
8 : #include <stdlib.h>
9 : #include <string.h>
10 : #include <time.h>
11 :
12 : #include "native_client/src/include/nacl_macros.h"
13 : #include "native_client/src/include/portability.h"
14 : #include "native_client/src/trusted/nacl_base/nacl_refcount.h"
15 : #include "native_client/src/trusted/service_runtime/nacl_error_gio.h"
16 :
17 : #define MAX_WRITE_CHUNK_SIZE 32
18 :
19 : static int g_verbosity = 0;
20 : static size_t g_output_bytes_min = 32;
21 : static size_t g_output_bytes_max = 2048;
22 :
23 : struct TailGio {
24 : struct GioVtbl const *vtbl;
25 : char buffer[NACL_ERROR_GIO_MAX_BYTES];
26 : size_t num_bytes;
27 : size_t total_output_bytes;
28 : };
29 :
30 : struct GioVtbl const kTailGioVtbl;
31 :
32 1 : static int TailGioCtor(struct TailGio *self) {
33 1 : self->vtbl = &kTailGioVtbl;
34 1 : self->num_bytes = 0;
35 1 : self->total_output_bytes = 0;
36 1 : return 1;
37 1 : }
38 :
39 1 : static void TailGioDtor(struct Gio *vself) {
40 : UNREFERENCED_PARAMETER(vself);
41 : return;
42 1 : }
43 :
44 0 : static ssize_t TailGioRead(struct Gio *vself, void *buf, size_t count) {
45 : UNREFERENCED_PARAMETER(vself);
46 : UNREFERENCED_PARAMETER(buf);
47 : UNREFERENCED_PARAMETER(count);
48 0 : return 0;
49 0 : }
50 :
51 1 : static ssize_t TailGioWrite(struct Gio *vself, void const *buf, size_t count) {
52 1 : struct TailGio *self = (struct TailGio *) vself;
53 :
54 1 : if (count > NACL_ARRAY_SIZE(self->buffer)) {
55 : memcpy(self->buffer, (char *) buf + count - NACL_ARRAY_SIZE(self->buffer),
56 0 : NACL_ARRAY_SIZE(self->buffer));
57 0 : self->num_bytes = NACL_ARRAY_SIZE(self->buffer);
58 0 : } else {
59 : /* 0 <= count <= NACL_ARRAY_SIZE(self->buffer) */
60 1 : size_t max_from_buffer = NACL_ARRAY_SIZE(self->buffer) - count;
61 :
62 : /* count + max_from_buffer == NACL_ARRAY_SIZE(self->buffer) */
63 : /* 0 <= max_from_buffer <= NACL_ARRAY_SIZE(self->buffer) */
64 1 : if (max_from_buffer < self->num_bytes) {
65 : memmove(self->buffer,
66 : self->buffer + self->num_bytes - max_from_buffer,
67 1 : max_from_buffer);
68 : memcpy(self->buffer + max_from_buffer,
69 1 : buf, count);
70 1 : self->num_bytes = NACL_ARRAY_SIZE(self->buffer);
71 1 : } else {
72 : /*
73 : * might still be a partial buffer:
74 : *
75 : * self->num_bytes <= max_from_buffer
76 : * count == NACL_ARRAY_SIZE(self->buffer) - max_from_buffer
77 : * therefore self->num_bytes + count <= NACL_ARRAY_SIZE(self->buffer)
78 : */
79 : memcpy(self->buffer + self->num_bytes,
80 1 : buf, count);
81 1 : self->num_bytes += count;
82 : }
83 : }
84 1 : self->total_output_bytes += count;
85 1 : return count;
86 1 : }
87 :
88 : static off_t TailGioSeek(struct Gio *vself,
89 : off_t offset,
90 0 : int whence) {
91 : UNREFERENCED_PARAMETER(vself);
92 : UNREFERENCED_PARAMETER(offset);
93 : UNREFERENCED_PARAMETER(whence);
94 0 : return 0;
95 0 : }
96 :
97 0 : static int TailGioFlush(struct Gio *vself) {
98 : UNREFERENCED_PARAMETER(vself);
99 0 : return 0;
100 0 : }
101 :
102 0 : static int TailGioClose(struct Gio *vself) {
103 : UNREFERENCED_PARAMETER(vself);
104 0 : return 0;
105 0 : }
106 :
107 : struct GioVtbl const kTailGioVtbl = {
108 : TailGioDtor,
109 : TailGioRead,
110 : TailGioWrite,
111 : TailGioSeek,
112 : TailGioFlush,
113 : TailGioClose,
114 : };
115 :
116 1 : struct NaClErrorGio *TestGioFactory(void) {
117 1 : struct TailGio *tgio = NULL;
118 1 : struct NaClErrorGio *negio = NULL;
119 :
120 1 : if (NULL == (tgio = (struct TailGio *) malloc(sizeof *tgio))) {
121 0 : fprintf(stderr, "TestGioFactory: Out of memory for tgio\n");
122 0 : goto giveup;
123 : }
124 1 : if (NULL == (negio = (struct NaClErrorGio *) malloc(sizeof *negio))) {
125 0 : fprintf(stderr, "TestGioFactory: Out of memory for negio\n");
126 0 : goto giveup;
127 : }
128 1 : if (!TailGioCtor(tgio)) {
129 0 : fprintf(stderr, "TestGioFactory: TailGioCtor failed\n");
130 0 : goto giveup;
131 : }
132 1 : if (!NaClErrorGioCtor(negio, (struct Gio *) tgio)) {
133 0 : fprintf(stderr, "TestGioFactory: NaClErrorGioCtor failed\n");
134 0 : goto giveup_dtor_tgio;
135 : }
136 1 : return negio;
137 :
138 : giveup_dtor_tgio:
139 0 : (*NACL_VTBL(Gio, tgio)->Dtor)((struct Gio *) tgio);
140 : giveup:
141 0 : free(negio);
142 0 : free(tgio);
143 0 : return NULL;
144 1 : }
145 :
146 1 : void TestGioRecycler(struct NaClErrorGio *trash) {
147 : struct TailGio *tgio;
148 :
149 1 : if (NULL == trash) {
150 0 : return;
151 : }
152 1 : tgio = (struct TailGio *) trash->pass_through;
153 : /* violate abstraction; this is test code */
154 1 : (*NACL_VTBL(Gio, trash)->Dtor)((struct Gio *) trash);
155 1 : free(trash);
156 1 : (*NACL_VTBL(Gio, tgio)->Dtor)((struct Gio *) tgio);
157 1 : free(tgio);
158 1 : }
159 :
160 1 : size_t UnitTest_BasicCtorDtor(void) {
161 : struct NaClErrorGio neg;
162 1 : size_t num_errors = 0;
163 :
164 1 : if (g_verbosity > 0) {
165 0 : printf("UnitTest_BasicCtorDtor\n");
166 : }
167 1 : if (!NaClErrorGioCtor(&neg, NULL)) {
168 0 : ++num_errors;
169 0 : fprintf(stderr, "UnitTest_BasicCtorDtor: could not construct\n");
170 0 : goto giveup;
171 : }
172 1 : (*NACL_VTBL(Gio, &neg)->Dtor)((struct Gio *) &neg);
173 : giveup:
174 1 : if (g_verbosity) {
175 0 : printf("UnitTest_BasicCtorDtor: %"NACL_PRIuS" error(s)\n", num_errors);
176 : }
177 1 : return num_errors;
178 1 : }
179 :
180 1 : size_t UnitTest_WeirdBuffers(void) {
181 : struct NaClErrorGio *neg;
182 1 : size_t num_errors = 0;
183 : size_t written;
184 1 : char const ktest_string[] = "123456789";
185 1 : size_t test_string_len = strlen(ktest_string);
186 : char small_buffer[1];
187 : char large_buffer[10 * NACL_ERROR_GIO_MAX_BYTES + 13];
188 : /* a much larger buffer, in case indexing calculations go awry */
189 : size_t actual;
190 : size_t ix;
191 :
192 1 : if (g_verbosity > 0) {
193 0 : printf("UnitTest_WeirdBuffers\n");
194 : }
195 1 : neg = TestGioFactory();
196 1 : if (NULL == neg) {
197 0 : ++num_errors;
198 0 : fprintf(stderr, "UnitTest_WeirdBuffers: could not construct\n");
199 0 : goto giveup;
200 : }
201 1 : if (0 != NaClErrorGioGetOutput(neg, (char *) NULL, 0)) {
202 0 : ++num_errors;
203 : fprintf(stderr,
204 : "UnitTest_WeirdBuffers: NULL buffer for NaClErrorGioGetOutput"
205 0 : " failed\n");
206 0 : goto giveup;
207 : }
208 : if (0 != NaClErrorGioGetOutput(neg,
209 : small_buffer,
210 1 : NACL_ARRAY_SIZE(small_buffer))) {
211 0 : ++num_errors;
212 : fprintf(stderr,
213 0 : "UnitTest_WeirdBuffers: small buffer with no output failed\n");
214 0 : goto giveup;
215 : }
216 :
217 : /* now make the buffer non-empty */
218 :
219 : written = (*NACL_VTBL(Gio, neg)->Write)((struct Gio *) neg,
220 1 : ktest_string, test_string_len);
221 1 : if (test_string_len != written) {
222 0 : ++num_errors;
223 : fprintf(stderr,
224 : "UnitTest_WeirdBuffers: writing %"NACL_PRIuS" bytes failed?!?\n",
225 0 : test_string_len);
226 0 : goto giveup;
227 : }
228 1 : small_buffer[0] = 'x'; /* different from '1' */
229 : actual = NaClErrorGioGetOutput(neg,
230 1 : small_buffer, NACL_ARRAY_SIZE(small_buffer));
231 1 : if (test_string_len != actual) {
232 0 : ++num_errors;
233 : fprintf(stderr,
234 : ("UnitTest_WeirdBuffers: too small buffer did not report actual"
235 : " required size, expected %"NACL_PRIuS", got %"NACL_PRIuS"\n"),
236 0 : test_string_len, actual);
237 0 : goto giveup;
238 : }
239 1 : if (small_buffer[0] != ktest_string[0]) {
240 0 : ++num_errors;
241 : fprintf(stderr,
242 : ("UnitTest_WeirdBuffers: partial buffer not filled, expected"
243 : " '1', got '%c'\n"),
244 0 : small_buffer[0]);
245 0 : goto giveup;
246 : }
247 : actual = NaClErrorGioGetOutput(neg,
248 1 : large_buffer, NACL_ARRAY_SIZE(large_buffer));
249 1 : if (test_string_len != actual) {
250 0 : ++num_errors;
251 : fprintf(stderr,
252 : ("UnitTest_WeirdBuffers: large buffer fill did not report actual"
253 : " output size, expected %"NACL_PRIuS", got %"NACL_PRIuS"\n"),
254 0 : test_string_len, actual);
255 0 : goto giveup;
256 : }
257 1 : for (ix = 0; ix < test_string_len; ++ix) {
258 1 : if (large_buffer[ix] != ktest_string[ix]) {
259 0 : ++num_errors;
260 : fprintf(stderr,
261 : ("UnitTest_WeirdBuffers: large buffer content wrong at ix %"
262 : NACL_PRIuS": expected '%c', got '%c'\n"),
263 0 : ix, 0xff & ktest_string[ix], 0xff & large_buffer[ix]);
264 : }
265 1 : }
266 : giveup:
267 1 : TestGioRecycler(neg);
268 1 : if (g_verbosity) {
269 0 : printf("UnitTest_BasicCtorDtor: %"NACL_PRIuS" error(s)\n", num_errors);
270 : }
271 1 : return num_errors;
272 1 : }
273 :
274 1 : size_t UnitTest_GenerateRandomOutput(void) {
275 : struct NaClErrorGio *neg;
276 1 : size_t num_errors = 0;
277 : char output_tail[NACL_ERROR_GIO_MAX_BYTES];
278 : size_t output_bytes;
279 : size_t output_discarded_bytes;
280 : size_t output_saved_tail_bytes;
281 : size_t ix;
282 : size_t write_chunk_size;
283 : char write_chunk[MAX_WRITE_CHUNK_SIZE];
284 : size_t jx;
285 : ssize_t written;
286 : char error_gio_content[NACL_ERROR_GIO_MAX_BYTES];
287 : size_t error_gio_content_bytes;
288 :
289 1 : if (g_verbosity > 0) {
290 0 : printf("UnitTest_GenerateRandomOutput\n");
291 : }
292 :
293 1 : neg = TestGioFactory();
294 1 : if (NULL == neg) {
295 : fprintf(stderr,
296 : ("UnitTest_GenerateRandomOutput: TestGioFactory"
297 0 : " construction failed\n"));
298 0 : ++num_errors;
299 0 : goto giveup;
300 : }
301 :
302 : output_bytes = g_output_bytes_min +
303 1 : (rand() % (g_output_bytes_max - g_output_bytes_min));
304 :
305 1 : if (output_bytes > NACL_ARRAY_SIZE(output_tail)) {
306 1 : output_saved_tail_bytes = NACL_ARRAY_SIZE(output_tail);
307 1 : output_discarded_bytes = output_bytes - output_saved_tail_bytes;
308 1 : } else {
309 1 : output_saved_tail_bytes = output_bytes;
310 1 : output_discarded_bytes = 0;
311 : }
312 :
313 1 : for (ix = 0; ix < output_discarded_bytes; ) {
314 1 : write_chunk_size = rand() % NACL_ARRAY_SIZE(write_chunk);
315 1 : if (write_chunk_size > output_discarded_bytes - ix) {
316 1 : write_chunk_size = output_discarded_bytes - ix;
317 : }
318 1 : for (jx = 0; jx < write_chunk_size; ++jx) {
319 1 : write_chunk[jx] = rand(); /* not efficient use of rng */
320 1 : }
321 : written = (*NACL_VTBL(Gio, neg)->Write)((struct Gio *) neg,
322 1 : write_chunk, write_chunk_size);
323 1 : if (written < 0) {
324 0 : ++num_errors;
325 : fprintf(stderr,
326 : ("UnitTest_GenerateRandomOutput: Write method returned %"
327 : NACL_PRIdS"\n"),
328 0 : written);
329 0 : goto abort_test;
330 : }
331 1 : ix += (size_t) written;
332 1 : }
333 1 : for (ix = 0; ix < output_saved_tail_bytes; ) {
334 1 : write_chunk_size = rand() % NACL_ARRAY_SIZE(write_chunk);
335 1 : if (write_chunk_size > output_saved_tail_bytes - ix) {
336 1 : write_chunk_size = output_saved_tail_bytes - ix;
337 : }
338 1 : for (jx = 0; jx < write_chunk_size; ++jx) {
339 1 : write_chunk[jx] = rand(); /* not efficient use of rng */
340 1 : }
341 : written = (*NACL_VTBL(Gio, neg)->Write)((struct Gio *) neg,
342 1 : write_chunk, write_chunk_size);
343 1 : if (written < 0) {
344 0 : ++num_errors;
345 : fprintf(stderr,
346 : ("UnitTest_GenerateRandomOutput: Write method returned %"
347 : NACL_PRIdS"\n"),
348 0 : written);
349 0 : goto abort_test;
350 : }
351 1 : for (jx = 0; jx < (size_t) written; ++jx) {
352 1 : output_tail[ix + jx] = write_chunk[jx];
353 1 : }
354 1 : ix += (size_t) written;
355 1 : }
356 : /*
357 : * Check that the final output Gio actually captured the number of bytes
358 : * that we think should have been captured.
359 : */
360 : if (output_saved_tail_bytes !=
361 1 : ((struct TailGio *) neg->pass_through)->num_bytes) {
362 0 : ++num_errors;
363 : fprintf(stderr,
364 : ("UnitTest_GenerateRandomOutput: TailGio buffer wrong captured"
365 : " size: got %"NACL_PRIuS", expected %"NACL_PRIuS"!\n"),
366 : ((struct TailGio *) neg->pass_through)->num_bytes,
367 0 : output_saved_tail_bytes);
368 : }
369 : /*
370 : * Check that the final output Gio actually saw the number of bytes
371 : * that we think should have been output.
372 : */
373 : if (output_bytes !=
374 1 : ((struct TailGio *) neg->pass_through)->total_output_bytes) {
375 0 : ++num_errors;
376 : fprintf(stderr,
377 : ("UnitTest_GenerateRandomOutput: TailGio buffer wrong total"
378 : " output size: got %"NACL_PRIuS", expected %"NACL_PRIuS"!\n"),
379 : ((struct TailGio *) neg->pass_through)->total_output_bytes,
380 0 : output_bytes);
381 : }
382 : /*
383 : * Compare output_tail with NaClErrorGioGetOutput output, and with
384 : * TailGio buffer.
385 : */
386 : error_gio_content_bytes =
387 : NaClErrorGioGetOutput(neg, error_gio_content,
388 1 : NACL_ARRAY_SIZE(error_gio_content));
389 1 : if (output_saved_tail_bytes != error_gio_content_bytes) {
390 0 : ++num_errors;
391 : fprintf(stderr,
392 : ("UnitTest_GenerateRandomOutput: NaClErrorGio buffer wrong size:"
393 : " got %"NACL_PRIuS", expected %"NACL_PRIuS"!\n"),
394 0 : error_gio_content_bytes, output_saved_tail_bytes);
395 : }
396 1 : for (ix = 0; ix < error_gio_content_bytes; ++ix) {
397 1 : if (output_tail[ix] != error_gio_content[ix]) {
398 : fprintf(stderr,
399 : ("UnitTest_GenerateRandomOutput: byte %"NACL_PRIuS
400 : ": wrote %02x, got %02x\n"),
401 0 : ix, 0xff & output_tail[ix], 0xff & error_gio_content[ix]);
402 0 : ++num_errors;
403 : }
404 : if (output_tail[ix] !=
405 1 : ((struct TailGio *) neg->pass_through)->buffer[ix]) {
406 : fprintf(stderr,
407 : ("UnitTest_GenerateRandomOutput: byte %"NACL_PRIuS
408 : ": wrote %02x, wrote through %02x\n"),
409 0 : ix, 0xff & output_tail[ix], 0xff & error_gio_content[ix]);
410 0 : ++num_errors;
411 : }
412 1 : }
413 : abort_test:
414 1 : TestGioRecycler(neg);
415 : giveup:
416 1 : if (g_verbosity > 0) {
417 : printf("UnitTest_GenerateRandomOutput: %"NACL_PRIuS" error(s)\n",
418 0 : num_errors);
419 : }
420 1 : return num_errors;
421 1 : }
422 :
423 : /*
424 : * This test could use gtest, but does not. Here is why:
425 : *
426 : * gtest does not have a way to specify a test parameter such as an
427 : * RNG seed from the command line -- except by using internal APIs to
428 : * specify a --gtest_<foo> flag, or by a side channel such as using an
429 : * environment variable. This makes randomized tests that can serve
430 : * to do API fuzzing during development as well as act as a quick
431 : * unittest more awkward to implement. Additionally, while gtest
432 : * itself has the ability to set an RNG seed, it is gtest's RNG for
433 : * shuffling the order of tests to be run -- there shouldn't be
434 : * interactions between gtest's RNG usage and the test's RNG usage, so
435 : * sharing the RNG and making use of gtest's ability to set RNG seed
436 : * is not a great idea. Furthermore, this test should *never* fail --
437 : * the xml output for getting test history (e.g., to deal with flaky
438 : * tests) is useless: if this test fails, somebody must have modified
439 : * the code in an erroneous way, and the change should be rolled back.
440 : */
441 1 : int main(int ac, char **av) {
442 : int opt;
443 1 : size_t num_errors = 0;
444 1 : size_t num_iter = 100000;
445 : size_t iter;
446 1 : unsigned seed = (unsigned) time((time_t *) NULL);
447 :
448 1 : while (-1 != (opt = getopt(ac, av, "b:B:n:s:v"))) {
449 1 : switch (opt) {
450 : case 'b':
451 0 : g_output_bytes_min = strtoul(optarg, (char **) NULL, 0);
452 0 : break;
453 : case 'B':
454 0 : g_output_bytes_max = strtoul(optarg, (char **) NULL, 0);
455 0 : break;
456 : case 'n':
457 1 : num_iter = strtoul(optarg, (char **) NULL, 0);
458 1 : break;
459 : case 's':
460 0 : seed = strtoul(optarg, (char **) NULL, 0);
461 : case 'v':
462 0 : ++g_verbosity;
463 : break;
464 : }
465 1 : }
466 1 : if (g_output_bytes_max < g_output_bytes_min) {
467 : fprintf(stderr,
468 : "nacl_error_gio_test: -b min_bytes value should not be larger"
469 0 : " than -B max_bytes value\n");
470 0 : return 1;
471 : }
472 1 : srand(seed);
473 1 : printf("seed %u\n", seed);
474 :
475 1 : num_errors += UnitTest_BasicCtorDtor();
476 1 : num_errors += UnitTest_WeirdBuffers();
477 :
478 1 : for (iter = 0; iter < num_iter; ++iter) {
479 1 : num_errors += UnitTest_GenerateRandomOutput();
480 1 : }
481 :
482 1 : return num_errors;
483 1 : }
|