1 /*
2 ** Common functionality for tests.
3 **/
4
5 #ifndef UPB_TEST_UTIL_H_
6 #define UPB_TEST_UTIL_H_
7
8 #include <stdio.h>
9 #include <math.h>
10 #include "tests/upb_test.h"
11 #include "upb/sink.h"
12
13 #include "upb/port_def.inc"
14
15 #ifdef __cplusplus
16
17 upb_bufhandle global_handle;
18
19 /* A convenience class for parser tests. Provides some useful features:
20 *
21 * - can support multiple calls to parse, to test the parser's handling
22 * of buffer seams.
23 *
24 * - can output verbose output about each parse call when requested, for
25 * ease of debugging.
26 *
27 * - can pass NULL for skipped regions of the input if requested.
28 *
29 * - allocates and passes a separate buffer for each parsed region, to
30 * ensure that the parser is not erroneously overreading its buffer.
31 */
32 class VerboseParserEnvironment {
33 public:
34 /* Pass verbose=true to print detailed diagnostics to stderr. */
VerboseParserEnvironment(bool verbose)35 VerboseParserEnvironment(bool verbose) : verbose_(verbose) {}
36
Reset(const char * buf,size_t len,bool may_skip,bool expect_error)37 void Reset(const char *buf, size_t len, bool may_skip, bool expect_error) {
38 buf_ = buf;
39 len_ = len;
40 ofs_ = 0;
41 expect_error_ = expect_error;
42 end_ok_set_ = false;
43 skip_until_ = may_skip ? 0 : -1;
44 skipped_with_null_ = false;
45 }
46
47 /* The user should call a series of:
48 *
49 * Reset(buf, len, may_skip);
50 * Start()
51 * ParseBuffer(X);
52 * ParseBuffer(Y);
53 * // Repeat ParseBuffer as desired, but last call should pass -1.
54 * ParseBuffer(-1);
55 * End();
56 */
57
58
Start()59 bool Start() {
60 if (verbose_) {
61 fprintf(stderr, "Calling start()\n");
62 }
63 return sink_.Start(len_, &subc_);
64 }
65
End()66 bool End() {
67 if (verbose_) {
68 fprintf(stderr, "Calling end()\n");
69 }
70 end_ok_ = sink_.End();
71 end_ok_set_ = true;
72
73 return end_ok_;
74 }
75
CheckConsistency()76 bool CheckConsistency() {
77 /* If we called end (which we should only do when previous bytes are fully
78 * accepted), then end() should return true iff there were no errors. */
79 if (end_ok_set_ && end_ok_ != status_.ok()) {
80 fprintf(stderr, "End() status and saw_error didn't match.\n");
81 return false;
82 }
83
84 if (expect_error_ && status_.ok()) {
85 fprintf(stderr, "Expected error but saw none.\n");
86 return false;
87 }
88
89 if (!status_.ok()) {
90 if (expect_error_ && verbose_) {
91 fprintf(stderr, "Encountered error, as expected: %s",
92 status_.error_message());
93 } else if (!expect_error_) {
94 fprintf(stderr, "Encountered unexpected error: %s",
95 status_.error_message());
96 return false;
97 }
98 }
99
100 return true;
101 }
102
ParseBuffer(int bytes)103 bool ParseBuffer(int bytes) {
104 if (bytes < 0) {
105 bytes = (int)(len_ - ofs_);
106 }
107
108 ASSERT((size_t)bytes <= (len_ - ofs_));
109
110 /* Copy buffer into a separate, temporary buffer.
111 * This is necessary to verify that the parser is not erroneously
112 * reading outside the specified bounds. */
113 char *buf2 = NULL;
114
115 if ((int)(ofs_ + bytes) <= skip_until_) {
116 skipped_with_null_ = true;
117 } else {
118 buf2 = (char*)malloc(bytes);
119 UPB_ASSERT(buf2);
120 memcpy(buf2, buf_ + ofs_, bytes);
121 }
122
123 if (buf2 == NULL && bytes == 0) {
124 /* Decoders dont' support buf=NULL, bytes=0. */
125 return true;
126 }
127
128 if (verbose_) {
129 fprintf(stderr, "Calling parse(%u) for bytes %u-%u of the input\n",
130 (unsigned)bytes, (unsigned)ofs_, (unsigned)(ofs_ + bytes));
131 }
132
133 int parsed = (int)sink_.PutBuffer(subc_, buf2, bytes, &global_handle);
134 free(buf2);
135
136 if (verbose_) {
137 if (parsed == bytes) {
138 fprintf(stderr,
139 "parse(%u) = %u, complete byte count indicates success\n",
140 (unsigned)bytes, (unsigned)bytes);
141 } else if (parsed > bytes) {
142 fprintf(stderr,
143 "parse(%u) = %u, long byte count indicates success and skip "
144 "of the next %u bytes\n",
145 (unsigned)bytes, (unsigned)parsed, (unsigned)(parsed - bytes));
146 } else {
147 fprintf(stderr,
148 "parse(%u) = %u, short byte count indicates failure; "
149 "last %u bytes were not consumed\n",
150 (unsigned)bytes, (unsigned)parsed, (unsigned)(bytes - parsed));
151 }
152 }
153
154 if (!status_.ok()) {
155 return false;
156 }
157
158 if (parsed > bytes && skip_until_ >= 0) {
159 skip_until_ = (int)(ofs_ + parsed);
160 }
161
162 ofs_ += UPB_MIN(parsed, bytes);
163
164 return true;
165 }
166
ResetBytesSink(upb::BytesSink sink)167 void ResetBytesSink(upb::BytesSink sink) {
168 sink_ = sink;
169 }
170
ofs()171 size_t ofs() { return ofs_; }
172
SkippedWithNull()173 bool SkippedWithNull() { return skipped_with_null_; }
174
arena()175 upb::Arena* arena() { return &arena_; }
status()176 upb::Status* status() { return &status_; }
177
178 private:
179 upb::Arena arena_;
180 upb::Status status_;
181 upb::BytesSink sink_;
182 const char* buf_;
183 size_t len_;
184 bool verbose_;
185 size_t ofs_;
186 void *subc_;
187 bool expect_error_;
188 bool end_ok_;
189 bool end_ok_set_;
190
191 /* When our parse call returns a value greater than the number of bytes
192 * we passed in, the decoder is indicating to us that the next N bytes
193 * in the stream are not needed and can be skipped. The user is allowed
194 * to pass a NULL buffer for those N bytes.
195 *
196 * skip_until_ is initially set to 0 if we should do this NULL-buffer
197 * skipping or -1 if we should not. If we are open to doing NULL-buffer
198 * skipping and we get an opportunity to do it, we set skip_until to the
199 * stream offset where we can skip until. The user can then test whether
200 * this happened by testing SkippedWithNull(). */
201 int skip_until_;
202 bool skipped_with_null_;
203 };
204
205 #endif /* __cplusplus */
206
upb_readfile(const char * filename,size_t * len)207 UPB_INLINE char *upb_readfile(const char *filename, size_t *len) {
208 long size;
209 char *buf;
210 FILE *f = fopen(filename, "rb");
211 if(!f) return NULL;
212 if(fseek(f, 0, SEEK_END) != 0) goto error;
213 size = ftell(f);
214 if(size < 0) goto error;
215 if(fseek(f, 0, SEEK_SET) != 0) goto error;
216 buf = (char*)malloc(size + 1);
217 if(size && fread(buf, size, 1, f) != 1) goto error;
218 fclose(f);
219 if (len) *len = size;
220 buf[size] = '\0';
221 return buf;
222
223 error:
224 fclose(f);
225 return NULL;
226 }
227
228 #include "upb/port_undef.inc"
229
230 #endif /* UPB_TEST_UTIL_H_ */
231