Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/third_party/closure/goog/labs/testing/json_fuzzing.js
2868 views
1
// Copyright 2015 The Closure Library Authors. All Rights Reserved.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
// http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS-IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
/**
16
* @fileoverview a fuzzing JSON generator.
17
*
18
* This class generates a random JSON-compatible array object under the
19
* following rules, (n) n being the relative weight of enum/discrete values
20
* of a stochastic variable:
21
* 1. Total number of elements for the generated JSON array: [1, 10)
22
* 2. Each element: with message (1), array (1)
23
* 3. Each message: number of fields: [0, 5); field type: with
24
* message (5), string (1), number (1), boolean (1), array (1), null (1)
25
* 4. Message may be nested, and will be terminated randomly with
26
* a max depth equal to 5
27
* 5. Each array: length [0, 5), and may be nested too
28
*/
29
30
goog.provide('goog.labs.testing.JsonFuzzing');
31
32
goog.require('goog.string');
33
goog.require('goog.testing.PseudoRandom');
34
35
36
37
/**
38
* The JSON fuzzing generator.
39
*
40
* @param {!goog.labs.testing.JsonFuzzing.Options=} opt_options Configuration
41
* for the fuzzing json generator.
42
* @param {number=} opt_seed The seed for the random generator.
43
* @constructor
44
* @struct
45
*/
46
goog.labs.testing.JsonFuzzing = function(opt_options, opt_seed) {
47
/**
48
* The config options.
49
* @private {!goog.labs.testing.JsonFuzzing.Options}
50
*/
51
this.options_ =
52
opt_options || {jsonSize: 10, numFields: 5, arraySize: 5, maxDepth: 5};
53
54
/**
55
* The random generator
56
* @private {!goog.testing.PseudoRandom}
57
*/
58
this.random_ = new goog.testing.PseudoRandom(opt_seed);
59
60
/**
61
* The depth limit, which defaults to 5.
62
* @private {number}
63
*/
64
this.maxDepth_ = this.options_.maxDepth;
65
};
66
67
68
/**
69
* Configuration spec.
70
*
71
* jsonSize: default to [1, 10) for the entire JSON object (array)
72
* numFields: default to [0, 5)
73
* arraySize: default to [0, 5) for the length of nested arrays
74
* maxDepth: default to 5
75
*
76
* @typedef {{
77
* jsonSize: number,
78
* numFields: number,
79
* arraySize: number,
80
* maxDepth: number
81
* }}
82
*/
83
goog.labs.testing.JsonFuzzing.Options;
84
85
86
/**
87
* Gets a fuzzily-generated JSON object (an array).
88
*
89
* TODO(user): whitespaces
90
*
91
* @return {!Array} A new JSON compliant array object.
92
*/
93
goog.labs.testing.JsonFuzzing.prototype.newArray = function() {
94
var result = [];
95
var depth = 0;
96
97
var maxSize = this.options_.jsonSize;
98
99
var size = this.nextInt(1, maxSize);
100
for (var i = 0; i < size; i++) {
101
result.push(this.nextElm_(depth));
102
}
103
104
return result;
105
};
106
107
108
/**
109
* Gets a new integer.
110
*
111
* @param {number} min Inclusive
112
* @param {number} max Exclusive
113
* @return {number} A random integer
114
*/
115
goog.labs.testing.JsonFuzzing.prototype.nextInt = function(min, max) {
116
var random = this.random_.random();
117
118
return Math.floor(random * (max - min)) + min;
119
};
120
121
122
/**
123
* Gets a new element type, randomly.
124
*
125
* @return {number} 0 for message and 1 for array.
126
* @private
127
*/
128
goog.labs.testing.JsonFuzzing.prototype.nextElmType_ = function() {
129
var random = this.random_.random();
130
131
if (random < 0.5) {
132
return 0;
133
} else {
134
return 1;
135
}
136
};
137
138
139
/**
140
* Enum type for the field type (of a message).
141
* @enum {number}
142
* @private
143
*/
144
goog.labs.testing.JsonFuzzing.FieldType_ = {
145
/**
146
* Message field.
147
*/
148
MESSAGE: 0,
149
150
/**
151
* Array field.
152
*/
153
ARRAY: 1,
154
155
/**
156
* String field.
157
*/
158
STRING: 2,
159
160
/**
161
* Numeric field.
162
*/
163
NUMBER: 3,
164
165
/**
166
* Boolean field.
167
*/
168
BOOLEAN: 4,
169
170
/**
171
* Null field.
172
*/
173
NULL: 5
174
};
175
176
177
/**
178
* Get a new field type, randomly.
179
*
180
* @return {!goog.labs.testing.JsonFuzzing.FieldType_} the field type.
181
* @private
182
*/
183
goog.labs.testing.JsonFuzzing.prototype.nextFieldType_ = function() {
184
var FieldType = goog.labs.testing.JsonFuzzing.FieldType_;
185
186
var random = this.random_.random();
187
188
if (random < 0.5) {
189
return FieldType.MESSAGE;
190
} else if (random < 0.6) {
191
return FieldType.ARRAY;
192
} else if (random < 0.7) {
193
return FieldType.STRING;
194
} else if (random < 0.8) {
195
return FieldType.NUMBER;
196
} else if (random < 0.9) {
197
return FieldType.BOOLEAN;
198
} else {
199
return FieldType.NULL;
200
}
201
};
202
203
204
/**
205
* Gets a new element.
206
*
207
* @param {number} depth The depth
208
* @return {!Object} a random element, msg or array
209
* @private
210
*/
211
goog.labs.testing.JsonFuzzing.prototype.nextElm_ = function(depth) {
212
switch (this.nextElmType_()) {
213
case 0:
214
return this.nextMessage_(depth);
215
case 1:
216
return this.nextArray_(depth);
217
default:
218
throw Error('invalid elm type encounted.');
219
}
220
};
221
222
223
/**
224
* Gets a new message.
225
*
226
* @param {number} depth The depth
227
* @return {!Object} a random message.
228
* @private
229
*/
230
goog.labs.testing.JsonFuzzing.prototype.nextMessage_ = function(depth) {
231
if (depth > this.maxDepth_) {
232
return {};
233
}
234
235
var numFields = this.options_.numFields;
236
237
var random_num = this.nextInt(0, numFields);
238
var result = {};
239
240
// TODO(user): unicode and random keys
241
for (var i = 0; i < random_num; i++) {
242
switch (this.nextFieldType_()) {
243
case 0:
244
result['f' + i] = this.nextMessage_(depth++);
245
continue;
246
case 1:
247
result['f' + i] = this.nextArray_(depth++);
248
continue;
249
case 2:
250
result['f' + i] = goog.string.getRandomString();
251
continue;
252
case 3:
253
result['f' + i] = this.nextNumber_();
254
continue;
255
case 4:
256
result['f' + i] = this.nextBoolean_();
257
continue;
258
case 5:
259
result['f' + i] = null;
260
continue;
261
default:
262
throw Error('invalid field type encounted.');
263
}
264
}
265
266
return result;
267
};
268
269
270
/**
271
* Gets a new array.
272
*
273
* @param {number} depth The depth
274
* @return {!Array} a random array.
275
* @private
276
*/
277
goog.labs.testing.JsonFuzzing.prototype.nextArray_ = function(depth) {
278
if (depth > this.maxDepth_) {
279
return [];
280
}
281
282
var size = this.options_.arraySize;
283
284
var random_size = this.nextInt(0, size);
285
var result = [];
286
287
// mixed content
288
for (var i = 0; i < random_size; i++) {
289
switch (this.nextFieldType_()) {
290
case 0:
291
result.push(this.nextMessage_(depth++));
292
continue;
293
case 1:
294
result.push(this.nextArray_(depth++));
295
continue;
296
case 2:
297
result.push(goog.string.getRandomString());
298
continue;
299
case 3:
300
result.push(this.nextNumber_());
301
continue;
302
case 4:
303
result.push(this.nextBoolean_());
304
continue;
305
case 5:
306
result.push(null);
307
continue;
308
default:
309
throw Error('invalid field type encounted.');
310
}
311
}
312
313
return result;
314
};
315
316
317
/**
318
* Gets a new boolean.
319
*
320
* @return {boolean} a random boolean.
321
* @private
322
*/
323
goog.labs.testing.JsonFuzzing.prototype.nextBoolean_ = function() {
324
var random = this.random_.random();
325
326
return random < 0.5;
327
};
328
329
330
/**
331
* Gets a new number.
332
*
333
* @return {number} a random number..
334
* @private
335
*/
336
goog.labs.testing.JsonFuzzing.prototype.nextNumber_ = function() {
337
var result = this.random_.random();
338
339
var random = this.random_.random();
340
if (random < 0.5) {
341
result *= 1000;
342
}
343
344
random = this.random_.random();
345
if (random < 0.5) {
346
result = Math.floor(result);
347
}
348
349
random = this.random_.random();
350
if (random < 0.5) {
351
result *= -1;
352
}
353
354
// TODO(user); more random numbers
355
356
return result;
357
};
358
359